Topic: return/exit from main()


Author: kjb@cs.brown.edu (Ken Basye)
Date: 31 Jan 92 15:34:18 GMT
Raw View
According to the ARM, returning from main has the same effect as
calling exit().  However, in ATT 3.0 (at least), the effect is not
quite the same; a call to return destroys local objects, while a call
to exit() does not.  Thus the compiler (but not the ARM) treats main
as it would any other function, that is, a return exits the scope of
the function (and hence local objects are destroyed), while a call to
exit() is presumably a function call that never returns, so the scope
is never exited (and hence local objects are *not* destroyed).

The local consensus is that the compiler is doing the right thing, and
that the ARM is "wrong" in asserting that a return from main has the
same effect as calling exit.  If the compiler were to treat a return
from main as a call to exit(), a programmer who needed both to return a
particular value from main and to have local objects destroyed would
have to introduce an inner scope specifically for this purpose.

Internet/CSnet  kjb@cs.brown.edu     U.S. MAIL  Ken Basye
UUCP            uunet!brunix!kjb                Box 1910
                                                Dept. of Computer Science
                                                Brown University
                                                Providence, RI  02912




Author: steve@taumet.com (Stephen D. Clamage)
Date: 1 Feb 92 18:12:43 GMT
Raw View
kjb@cs.brown.edu (Ken Basye) writes:

>According to the ARM, returning from main has the same effect as
>calling exit().  However, in ATT 3.0 (at least), the effect is not
>quite the same; a call to return destroys local objects, while a call
>to exit() does not....

>The local consensus is that the compiler is doing the right thing, and
>that the ARM is "wrong" in asserting that a return from main has the
>same effect as calling exit....

I believe this analysis has to be correct, and the statement in the
ARM is just left over from the ANSI C standard.

If exit() is called from a nested function call, we have to say that
local automatic objects remain un-destoyed all the way up the stack.

If exit() is called from main(), I think the same thing must be true:
automatic objects in main remain un-destroyed.  Otherwise, we must
treat exit() as built into the language, with very peculiar semantics.

Probably the ARM should just say that the value returned to the system
from 'exit(n)' is the same as from 'return n;' in main.
--

Steve Clamage, TauMetric Corp, steve@taumet.com




Author: cline@sun.soe.clarkson.edu (Marshall Cline)
Date: 4 Feb 92 09:16:41 GMT
Raw View
In article <kor6lpINNqfb@exodus.Eng.Sun.COM> chased@rbbb.Eng.Sun.COM (David Chase) writes:
>kjb@cs.brown.edu (Ken Basye) writes: >>
>Couldn't you treat "exit" as raising an exception that is caught by
>some wrapper around main?  This would destroy the local automatic
>objects.

I believe that the goal you seek (`destroy the local automatic objects') is
the problem with the above approach.  When an exception throw is unwinding
the stack, if a destructor throws an exception, `terminate()' is called,
which may call exit(int), which will throw the uncatchable exception, which
will try to destroy objects on the stack, which will throw another exception
etc.

Seems to me that exit(int) means `flush I/O buffers and DIE', where if I
wanted to make sure all local and global objects were destructed for their
global side effects, I could simply throw an exception which I didn't expect
anyone could catch; ex:

 class dont_you_dare_catch_this_exception { };
 void throw_uncatchable()
 {
   throw dont_you_dare_catch_this_exception();
 }

--
Marshall Cline
--
Marshall P. Cline, Ph.D. / Paradigm Shift, Inc / 65 N Main St/Norwood, NY 13668
cline@sun.soe.clarkson.edu / 315-353-4585 / FAX: 315-353-6100




Author: jbn@lulea.trab.se (Johan Bengtsson)
Date: 3 Feb 92 19:18:14 GMT
Raw View
Ken Basye (kjb@cs.brown.edu) writes:
<
< According to the ARM, returning from main has the same effect as
< calling exit().  However, in ATT 3.0 (at least), the effect is not
< quite the same; a call to return destroys local objects, while a call
< to exit() does not....
<
< The local consensus is that the compiler is doing the right thing, and
< that the ARM is "wrong" in asserting that a return from main has the
< same effect as calling exit....

 I agree that C++ 3.0 could hardly do it any other way, but...

 When exception handling arrives, shouldn't it be possible
 for exit() to unwind the stack, destroying all local objects?

 It seems to me that all the necessary context would be available.
--
-------------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden       |
| Johan.Bengtsson@lulea.trab.se; Voice:(+46)92075471; Fax:(+46)92075490       |
-------------------------------------------------------------------------------




Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 3 Feb 92 19:40:41 GMT
Raw View
kjb@cs.brown.edu (Ken Basye) writes: >>

>>According to the ARM, returning from main has the same effect as
>>calling exit().  However, in ATT 3.0 (at least), the effect is not
>>quite the same; a call to return destroys local objects, while a call
>>to exit() does not....

>>The local consensus is that the compiler is doing the right thing, and
>>that the ARM is "wrong" in asserting that a return from main has the
>>same effect as calling exit....

steve@taumet.com (Stephen D. Clamage) writes: >

>I believe this analysis has to be correct, and the statement in the
>ARM is just left over from the ANSI C standard.

>If exit() is called from a nested function call, we have to say that
>local automatic objects remain un-destoyed all the way up the stack.

>If exit() is called from main(), I think the same thing must be true:
>automatic objects in main remain un-destroyed.  Otherwise, we must
>treat exit() as built into the language, with very peculiar semantics.

Couldn't you treat "exit" as raising an exception that is caught by
some wrapper around main?  This would destroy the local automatic
objects.  Of course, this means that an "exit exception" could
actually be caught by a catch-all clause, but this could be "fixed" by

(a) documenting the behavior or
(b) defining a new class of exceptions that can only be caught "by
    name".

If there were (is there?) subclassing of exception types, the
nastiness of this problem will be reduced, because there is less need
to insert clauses that catch ALL exceptions.  Of course, this is not
entirely compatible with C semantics, but I don't assume that this is
incredibly important -- a C program embedded in a C++ run-time won't
be able to tell the difference, which ought to be good enough.

David Chase
Sun




Author: jac@moonshine.llnl.gov (James A. Crotinger)
Date: 4 Feb 92 17:28:34 GMT
Raw View
cline@sun.soe.clarkson.edu (Marshall Cline) writes:
> I believe that the goal you seek (`destroy the local automatic objects') is
> the problem with the above approach.  When an exception throw is unwinding
> the stack, if a destructor throws an exception, `terminate()' is called,
> which may call exit(int), which will throw the uncatchable exception, which
> will try to destroy objects on the stack, which will throw another exception
> etc.

> Seems to me that exit(int) means `flush I/O buffers and DIE',

  Actually, the C/UNIX meaining of "exit(int)" is "terminate a process
after performing cleanup". I would include unwinding the stack as part
of cleanup, if that is possible. abort() is the call which should be
made in dire circumstances (like an exception in a destructor). It is
not meant to be graceful (and it leaves you with a core dump, which is
likely what you want if you get an exception in a destructor).

  Jim
--
-------------------------------------------------/\--------------------------
James A. Crotinger     Lawrence Livermore N'Lab // \ The above views are mine
jac@moonshine.llnl.gov P.O. Box 808;  L-630 \\ //---\  and are not neces-
(510) 422-0259         Livermore CA  94550   \\/Amiga\  sarily those of LLNL.




Author: pat@frumious.uucp (Patrick Smith)
Date: Tue, 4 Feb 1992 04:12:09 GMT
Raw View
In article <3899@lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
| When exception handling arrives, shouldn't it be possible
| for exit() to unwind the stack, destroying all local objects?

[This was also suggested by David Chase in another message.]

I'd imagine this could be done, but I don't think it should be,
because:

1) It would break many existing programs which depend on the
   current behaviour of exit().

2) It's not necessary.  Any programmer who wants to terminate a
   program and destroy all local objects can throw an appropriate
   exception.  (Making sure that it will be rethrown whenever it's
   caught, of course.)

3) We would lose the ability to terminate the program and destroy
   global objects but not destroy local objects (at least, I can't
   think of anyway to do this without using exit() -- can anyone
   else?).

--
Patrick Smith
uunet.ca!frumious!pat
pat%frumious.uucp@uunet.ca




Author: jbn@lulea.trab.se (Johan Bengtsson)
Date: 4 Feb 92 17:43:37 GMT
Raw View
Patrick Smith (pat@frumious.uucp) writes:
: In article <3899@lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
: | When exception handling arrives, shouldn't it be possible
: | for exit() to unwind the stack, destroying all local objects?
:
: I'd imagine this could be done, but I don't think it should be,
: because:
:
: 1) It would break many existing programs which depend on the
:    current behaviour of exit().

 What on earth are these programs doing?  Destroy globals
 but not locals?  Please explain!

 As has been said in this thread, such programs are
 in violation with the language described in the ARM,
 so they are already broken.

 Why not use abort() instead?

: 2) It's not necessary.  Any programmer who wants to terminate a
:    program and destroy all local objects can throw an appropriate
:    exception.  (Making sure that it will be rethrown whenever it's
:    caught, of course.)

 You can't be sure about this.  Default exception
 handlers are not required to rethrow.

 But maybe you are right, perhaps exit() does not really belong
 in a C++=0 (pure:-) program.  Perhaps exceptions should be used
 instead.  If this view is adopted, then exit() should
 not destroy global objects either (C semantics).
--
-------------------------------------------------------------------------------
| Johan Bengtsson, Telia Research AB, Aurorum 6, S-951 75 Lulea, Sweden       |
| Johan.Bengtsson@lulea.trab.se; Voice:(+46)92075471; Fax:(+46)92075490       |
-------------------------------------------------------------------------------




Author: pat@frumious.uucp (Patrick Smith)
Date: Thu, 6 Feb 1992 02:35:39 GMT
Raw View
In article <3902@lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
|Patrick Smith (pat@frumious.uucp) writes:
|: In article <3899@lulea.trab.se> jbn@lulea.trab.se (Johan Bengtsson) writes:
|: | When exception handling arrives, shouldn't it be possible
|: | for exit() to unwind the stack, destroying all local objects?
|:
|: I'd imagine this could be done, but I don't think it should be,
|: because:
|:
|: 1) It would break many existing programs which depend on the
|:    current behaviour of exit().
|
| What on earth are these programs doing?  Destroy globals
| but not locals?  Please explain!

It's not so much a question of how programs should be written as of
how they ARE written.  Making a big change to the behaviour compilers
assign to exit() would surely break some of the programs which use
exit().  And destoying local objects would be a big change.

| As has been said in this thread, such programs are
| in violation with the language described in the ARM,
| so they are already broken.

Broken according to the ARM, yes.  Broken according to the way
most compilers implement exit(), no (to the best of my knowledge).
Does anyone know of any compiler in which returning from main()
and calling exit() in main() have the same effect on local variables?

Let's hope this difference between the compilers and the language
specification will be resolved by X3J16.  My personal preference
would be to accept the way the compilers do it.

| Why not use abort() instead?

Because this (usually) doesn't destroy static objects.
And because abort() isn't guaranteed to terminate the process;
it's only guaranteed not to return.  (You might be catching the
signal raised by abort().)

|: 2) It's not necessary.  Any programmer who wants to terminate a
|:    program and destroy all local objects can throw an appropriate
|:    exception.  (Making sure that it will be rethrown whenever it's
|:    caught, of course.)
|
| You can't be sure about this.  Default exception
| handlers are not required to rethrow.

If you have the source for the entire program, you can ensure that
all handlers rethrow this exception.  If not, you might have a
problem here.

| But maybe you are right, perhaps exit() does not really belong
| in a C++=0 (pure:-) program.  Perhaps exceptions should be used
| instead.  If this view is adopted, then exit() should
| not destroy global objects either (C semantics).

Perhaps it would be useful to provide three separate functions
to terminate a program (names made up off the top of my head):

leave() - destroys all automatic and static objects and calls
   functions registered with atexit()

exit()  - does not destroy automatic objects, does destroy static
   objects, calls functions registered with atexit()

panic() - stops the process immediately, without destroying anything
   and without calling the functions registered with atexit()

The last one might not be necessary; in most programs, abort() will
have a similar effect.

--
Patrick Smith
uunet.ca!frumious!pat
pat%frumious.uucp@uunet.ca