From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 26055 invoked by alias); 8 May 2003 19:37:19 -0000 Mailing-List: contact gcc-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Archive: List-Post: List-Help: Sender: gcc-owner@gcc.gnu.org Received: (qmail 26035 invoked from network); 8 May 2003 19:37:14 -0000 Received: from unknown (HELO lacrosse.corp.redhat.com) (66.187.233.200) by sources.redhat.com with SMTP; 8 May 2003 19:37:14 -0000 Received: from prospero.boston.redhat.com (sebastian-int.corp.redhat.com [172.16.52.221]) by lacrosse.corp.redhat.com (8.11.6/8.9.3) with ESMTP id h48Jb1i25280; Thu, 8 May 2003 15:37:01 -0400 Received: by prospero.boston.redhat.com (Postfix, from userid 4046) id 64010F8EB7; Thu, 8 May 2003 15:36:41 -0400 (EDT) To: Mark Mitchell Cc: Mike Stump , gcc@gcc.gnu.org, Jason Merrill Subject: Re: __attribute__((cleanup(function)) versus try/finally From: Jason Merrill In-Reply-To: <1052419221.3329.111.camel@minax.codesourcery.com> (Mark Mitchell's message of "08 May 2003 11:40:21 -0700") References: <1052419221.3329.111.camel@minax.codesourcery.com> Date: Thu, 08 May 2003 19:37:00 -0000 Message-ID: User-Agent: Gnus/5.090019 (Oort Gnus v0.19) Emacs/21.2 (gnu/linux) MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-SW-Source: 2003-05/txt/msg00823.txt.bz2 On 08 May 2003 11:40:21 -0700, Mark Mitchell wrote: > If we were designing a new language, I'd certainly agree with you that > exceptions are a good feature. When discussing C on a workstation-class > machine, I'd be more likely to agree with you, even though I think > adding exceptions to C is antithetical of that language's key design > goals. > > When discussing C for embedded systems, though, I just can't see it. I > fully concede that my scheme is less beautiful -- unless you see beauty > in the number of bytes saved. I argued in my last reply that sjlj EH can have the same cost as the existing pthread cleanup mechanism. Do you disagree? > I think what really happened here was that the pthreads designers wanted > to add pthread_atexit -- and then got carried away. I think they wanted exactly what they wrote. atexit isn't useful for cleanups of a fixed duration. > (In fact, so far as I can tell, a careful reading of > > http://www.unix.org/single_unix_specification/ > > would suggest that: > > pthread_cleanup_push (f, a); > goto l; > pthread_cleanup_pop (1); > l: > > is well-defined, and does not result in the cleanup being executed. > Presumably, this should be undefined behavior, as it does not work at > all with the sample implementation in the specification. Agreed. > The question of whether pthread_cleanup_push should create a new scope > is also important, at least in C99 and C++: is > > int i; > pthread_cleanup_push (f, a); > int i; > > valid, or not, or is this unspecified?) I'd say unspecified or valid. The spec talks about implementation as macros containing { and }. > Also, doesn't the execute argument to pthread_cleanup_pop mean that > try/finally isn't the right construct? > > I would think that the EH version of: > > pthread_cleanup_push (f, a); > g (); > pthread_cleanup_pop (x); > > would be: > > try { > g(); > } catch (...) { > f(a); > throw; > } > if (x) f(a); Hard to do that with macros; you need to store the push args into temporary variables. You'd need to do something like { void (*_f)(void *) = f; void *_a = a; try { g(); } catch (...) { _f (_a); throw; } if (x) _f (_a); } but nobody's advocating adding catch to C, are they? Besides, catch/rethrow is more expensive than running a cleanup. A try/finally implementation would look like { void (*_f)(void *) = f; void *_a = a; int _x = 1; try { g (); _x = x; } finally { if (_x) _f (_a); } } An implementation using C++ destructors would work similarly: struct cleanup { void (*_f)(void *); void *_a; bool _x; cleanup (void (*fn)(void *), void *arg): _f (fn), _a (arg), _x (true) {} ~cleanup () { if (_x) _f(_a); } }; ... { cleanup c (f, a); g (); c._x = x; } Or, using attribute (cleanup): struct cleanup { void (*_f)(void *); void *_a; int _x; }; void cleanup_dtor (struct cleanup *p) { if (p->_x) (p->_f)(p->_a); } ... { struct cleanup c __attribute ((__cleanup (cleanup_dtor))) = { f, a, 1 }; g (); c._x = x; } Any of these would do the job. All of them involve adding EH to C. I think that try/finally is the most elegant way to do that. Jason