This is the mail archive of the gcc-patches@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: [basic-improvements] try/finally support for c/c++ - more tests


On Fri, Nov 08, 2002 at 12:48:49PM +0000, Nicola Pero wrote:
> Well you're not a lone voice of insance conservatism, I actually think you
> are quite well giving voices to the puzzled C users, like me. :-)
> 
> In all this discussion, I didn't quite find discussed how it would work in
> C/ObjC, from a pure C/ObjC perspective.  Which IMO is essential to have
> the C extension approved.
> 
> The suggestion that this change is good because "it makes C look more like
> C++ so that it's easier to integrate C and C++" might be good for C++
> hackers and lovers, but it's definitely unconvincing for C/ObjC users with
> no interest in C++.

If you look at a typical system nowadays, C and C++ has to easily integrate
together, as C++ code often calls C routines (starting from libstdc++ using
C library functions where C library is most often written in C) and a lot of
C programs/libraries are using C++ libraries too.

> Shall we "finally try" to talk a bit about this C language extension from
> a C user perspective, without jumping into C++ every sentence ?
> 
> I've got a lot of doubts that this extension is good for C, at least as it
> is.
> 
> In C there is no exception handling, so it's common for C libraries to
> implement some sort of exception handling using setjmp/longjmp.
> 
> As far as I understand, the proposed feature wouldn't be enough powerful
> to replace the existing setjmp/longjmp based exception handling.

It has no intent to replace it, it is about cleanups when exception is
thrown, not the exception handling mechanism itself.

> Actually, the proposed feature would actually not work with the existing
> setjmp/longjmp based exception handling ... either you use the new feature
> (which is not enough powerful to provide exception handling), or you use
> setjmp/longjmp.

But it plays nicely with setjmp/longjmp_unwind. Code using pure
setjmp/longjmp nowadays cannot use any cleanups locally, all buffers you
allocate or locks you acquire have to be global, so that when setjmp returns
non-zero you can free them or release them. At least with current glibc
pthread_cleanup_* implementation, it even cannot use pthread_cleanup_*,
because libpthread will be seriously confused (usually segfault) after
you try longjmp for the first time skipping some frame which did
pthread_cleanup_push.

> As a C/ObjC user, I don't find this solution very satisfactorily.  We've
> been long waiting for "real" exception handling in C/ObjC ... and this is
> *not* it.

See the above. If you do:
jmp_buf b;
void foo (void)
{
  if (setjmp (b))
    {
      exception was raised;
    }
  bar ();
}
void bar (void)
{
  void *x = malloc (1024);
  __try
    {
      do_something_with_x
      baz ();
      do_something_else_with_x
    }
  __finally
    {
      free (x);
    }
}
void baz (void)
{
  if (some_weird_condition)
    longjmp_unwind (b);
}

then you IMHO have all you need for satisfactory pure C exception handling
with minimal overhead. With __thread variables you can put any
data you want into them at longjmp_unwind time (ie. throw someobject in C)
and check it at setjmp time.
Without __try/__finally but with longjmp_unwind you can still do this,
but see my other mail what needs to be done instead of that, making the code
quite unreadable.

> As far as I understand from the explanations on this thread, try/finally
> in C would only be used to cleanup before returning from a function, such
> as
> 
> try
> {
>  char *stringA = NULL;
>  char *stringB = NULL;
>  char *stringC = NULL;
> 
>  stringA = malloc (sizeof (char) * 20);
>  if (stringA == NULL) 
>    return;
> 
>  ...
> 
>  stringB = malloc (sizeof (char) * 20);
>  if (stringB == NULL) 
>    return;
> 
>  ...
> 
>  stringC = malloc (sizeof (char) * 20);
>  if (stringC == NULL) 
>    return;
> 
>  ...
> 
>  return;
> }
> finally
> {
>   if (stringA != NULL) 
>     free (stringA);
> 
>   if (stringB != NULL) 
>     free (stringB);
> 
>   if (stringC != NULL) 
>     free (stringC);
> }
> 
> It's nice as an example, except I can trivially do it already without any
> need for a C extension:
> 
>  char *stringA = NULL;
>  char *stringB = NULL;
>  char *stringC = NULL;
> 
>  stringA = malloc (sizeof (char) * 20);
>  if (stringA == NULL) 
>    goto finally;
> 
>  ...
> 
>  stringB = malloc (sizeof (char) * 20);
>    goto finally;
> 
>  ...
> 
>  stringC = malloc (sizeof (char) * 20);
>  if (stringC == NULL) 
>    goto finally;
> 
>  ...
> 
> finally:
> {
>   if (stringA != NULL) 
>     free (stringA);
> 
>   if (stringB != NULL) 
>     free (stringB);
> 
>   if (stringC != NULL) 
>     free (stringC);
> 
>   return;
> }

Sure, but:
a) typically in such functions they are not returning void, but some value,
   with __try/__finally you can leave the code as above, just add the
   return value to each return statement. With goto gem you need some
   function scope variable and set it first, then return,
   so if (stringC == NULL) return 134; becomes
   if (stringC == NULL) { ret = 134; goto finally; }
   which is less readable
b) if not all variables are function scope, the code gets much uglier
c) with __try/__finally, you get the cleanups with pthread cancellation
   or C++ exceptions thrown or longjmp_unwind for free. You don't have
   to worry about storing stringA, stringB, stringC not in
   separate variables, but into some local structure, doing
   pthread_cleanup_push at the beginning with address of that local structure
   and either having the finally block just do pthread_cleanup_pop(1)
   or pthread_cleanup_pop(0) with duplication of all the free calls into
   the code after finally label and in a separate function.

	Jakub


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