This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]