This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Re: __attribute__((cleanup(function)) versus try/finally
> There are no costs unless there are cleanups.
Yes -- but I'm talking about the situation where there are cleanups.
Concretely, consider the following C function:
void f() {
FILE *f1, *f2, *f3;
f1 = fopen ("/tmp/foo");
pthread_cleanup_push (fclose, f1);
g();
f2 = fopen ("/tmp/bar");
pthread_cleanup_push (fclose, f2);
g();
f3 = fopen ("/tmp/baz");
pthread_cleanup_push (fclose, f3);
g ();
pthread_cleanup_pop (1);
pthread_cleanup_pop (1);
pthread_cleanup_pop (1);
}
Now, with my scheme this function has no handler code; i.e, the EH
run-time library will never transfer control back to this function.
Even when using setjmp/longjmp the cost is just one setjmp at the start
of the function, so that the stack can be unwound.
If pthread_cleanup_push uses try/finally, it's my understanding that
there will be multiple calls to setjmp, before each of the calls to g.
And in the non-setjmp case, there will be landing pads and unwind
information for each of the exception regions, rather than just one for
the entire function.
> > Without setjmp/longjmp, you save mostly code space. You also improve
> > cleanup time (in that you need not transfer control to the local frame
> > of the function before running the cleanup), but this is a smaller
> > advantage.
>
> Which may or may not outweigh the advantages of having the cleanups in the
> function (code motion into the landing pad, inlining destructors, not
> forcing the argument into the stack).
For pthread_cleanup_push, this is generally not an issue; the cleanup is
a function pointer. Unless that function pointer is to an inline
function, you're going to make a call here anyhow; there's no real
advantage.
> Basically, what I'm saying is that the question of what sorts of cleanups
> to allow can be separated from the question of how to implement them. If
> we can set aside the implementation issues, it becomes just a question of
> language design, and I still think that try/finally is the elegant
> solution.
(There's nothing wrong with try/finally as a construct in the abstract.
I'm all for continuations, too! And real closures! If C had
templates, objects, exceptions, and namespaces, I'd love it to pieces!
Seriously, one of the few remaining objections to programming in C++ has
been "exceptions add too much overhead". Once we add unwind tables to C
-- as all of our proposals do, including mine -- I'm not really sure
what the point of programming in C is going to be any more. Maybe if
you really like the fact that "struct S {};" doesn't introduce a
typedef, or that "void f()" isn't a prototype. Maybe we should add
-fnothing-but-exceptions to G++, and just use that as the C compiler.)
Adding EH to C is a very dramatic change. The spirit of C is "no hidden
stuff." Calling exceptions "zero-cost" is a lie; we say that because
they require no additional instructions be executed unless an exception
is thrown, not because they actually have no cost. The unwind tables
are a significant cost, for example.
To that end, I've toyed with the following idea:
- When building a library written in C, provide two sets of entry points
for the globally visible functions: a version that is called from C, and
a version called from everything else. The non-C version of the entry
point calls setjmp. Then, you can unwind through C code with a single
longjmp, no unwind tables, no handlers. (You do this by saying "huh, no
unwind info for this frame; must be C; call the top entry on the longjmp
stack.) You actually only need this setjmp on those entry points that
are going to call things that might throw exceptions; in glibc, for
example, that means functions that call cancellation points, or take
callbacks (ala qsort).
- The cost of this scheme for a pure C program is exactly zero. No
unwind info, no setjmps, no nothing. You get correct mixed C++/C
semantics with slight cost; on entry to a C library from C++ code you
take a hit if you need to call setjmp.
This kind of solution is awfully attractive if you're on an embedded
system. (I want pthreads on my cell-phone, but I sure don't want to
have extra unwind info around. I want to be able to support C++, but if
it's not in use, I don't want to have to pay the cost of supporting C++
by my making my C libraries bigger.)
--
Mark Mitchell
CodeSourcery, LLC
mark@codesourcery.com