is there an atexit?

5,309 views
Skip to first unread message

David Roundy

unread,
Feb 20, 2010, 12:29:08 PM2/20/10
to golang-nuts
I've searched the package repository, and haven't found an atexit (or
rather AtExit) function or its equivalent... but it seems like it'd be
pretty painful to live without this functionality. Are there plans to
add this? It seems like a perfect complement to the new SetFinalizer,
so that we could use a bufio.Writer without worrying about calling
Flush(), just to avoid losing data.

Am I missing something?
--
David Roundy

Valentin Kasas

unread,
Feb 21, 2010, 7:43:57 AM2/21/10
to golang-nuts
Hi David

Actually there is such a termination mechanism with the defer
statement : http://golang.org/doc/go_spec.html#Defer_statements
that does the same work as the c++ std::atexit() function. BTW, I see
two slight differences between defer statements and atexit function
call :

- defer is not a function, so defering mutliple statements does not
lead to mutliple function calls
- defer statement can be put in any function body.

I think you should achieve the the atexit's job by simply defering
stuff in your main function.

best regards

V.K.

chris dollin

unread,
Feb 21, 2010, 9:52:09 AM2/21/10
to Valentin Kasas, golang-nuts

How do you propose to transmit the call to atexit -- which may occur anywhere in the executing code -- to the main function so that it can defer a call of the tobeatexited function?

--
Chris "allusive" Dollin

David Roundy

unread,
Feb 22, 2010, 1:00:23 PM2/22/10
to chris dollin, Valentin Kasas, golang-nuts

I'd presume you'd do this by deferring a call to a function variable,
and then you could just modify that variable in other functions, that
wouldn't be hard.

The problem is that the deferred statements are only executed if the
program terminates without calling os.Exit, which defeats most
possible uses of the atexit system call. It really seems like a
problem that requires runtime support (or at least exposing atexit
itself).
--
David Roundy

Russ Cox

unread,
Feb 22, 2010, 1:35:13 PM2/22/10
to David Roundy, golang-nuts
Atexit may make sense in single-threaded, short-lived
programs, but I am skeptical that it has a place in a
long-running multi-threaded server.

I've seen many C++ programs that hang on exit because
they're running global destructors that don't really need to
run, and those destructors are cleaning up and freeing
memory that would be reclaimed by the operating system
anyway, if only the program could get to the exit system call.
Compared to all that pain, needing to call Flush when you're
done with a buffer seems entirely reasonable and is
necessary anyway for correct execution of long-running
programs.

Even ignoring that problem, atexit introduces even more
threads of control, and you have to answer questions like
do all the other goroutines stop before the atexit handlers
run? If not, how do they avoid interfering? If so, what if
one holds a lock that the handler needs? And on and on.

I'm not at all inclined to add Atexit.

Russ

David Roundy

unread,
Feb 23, 2010, 10:44:20 AM2/23/10
to r...@golang.org, golang-nuts
On Mon, Feb 22, 2010 at 10:35 AM, Russ Cox <r...@golang.org> wrote:
> Atexit may make sense in single-threaded, short-lived
> programs, but I am skeptical that it has a place in a
> long-running multi-threaded server.

Hmmm. I wasn't aware that go wasn't intended to also be suitable for
simple short-lived programs. I had thought it was a general-purpose
programming language...

> I've seen many C++ programs that hang on exit because
> they're running global destructors that don't really need to
> run, and those destructors are cleaning up and freeing
> memory that would be reclaimed by the operating system
> anyway, if only the program could get to the exit system call.
> Compared to all that pain, needing to call Flush when you're
> done with a buffer seems entirely reasonable and is
> necessary anyway for correct execution of long-running
> programs.

Hmmm. I don't see how just because a feature could be abused, we
should have to put up with inferior libraries that are harder to use
properly. In particular, it'd be great to be able to use a
bufio.Writer as a drop-in replacement for other sorts of Writers. Or
to enable safer use of compress.zlib, so that compressed files would
be closed on exit.

> Even ignoring that problem, atexit introduces even more
> threads of control, and you have to answer questions like
> do all the other goroutines stop before the atexit handlers
> run?  If not, how do they avoid interfering?  If so, what if
> one holds a lock that the handler needs?  And on and on.

Hmmm. I understand there are interesting questions, but if
programmers write buggy code, I'm sure it isn't a surprise that their
code may misbehave.

> I'm not at all inclined to add Atexit.

That's unfortunate. It's reasonably simple to work around in one's
own program, simply by wrapping os.Exit, but it'd be nicer to be able
to have more robust libraries also.
--
David Roundy

Robert Johnstone

unread,
May 25, 2012, 10:30:38 AM5/25/12
to golan...@googlegroups.com, David Roundy, r...@golang.org
I don't think this is a good comparison.  The destructors in a C++ program are implicit, and the compiler is arranging for them all to be called (through various smart pointers) when it unwinds the stack and destroys global variables at the end of the program.  Atexit is an explicit request from the programmer to perform some clean-up when a program is terminated.  Additionally, a lot of that clean-up work is related to releasing memory, which would not apply to Go even if Go had deterministic destructors.

(However, I don't see a reason to add atexit either.)

Robert Johnstone

unread,
May 25, 2012, 10:48:05 AM5/25/12
to golan...@googlegroups.com
I have found that the init function for modules is pretty limited.  If anything fails, your only option is to panic.  Therefore, you typically need a separate public function for initialization.  The pattern then becomes to use defer to arrange for the termination.  i.e.

err := foo.Initialize(...)
if err!=nil { ... }
defer foo.Terminate()

This does not defend against calls to os.Exit, but I would be very upset with any libraries that called os.Exit directly.  Libraries should report an error condition, and not decide to abort the program.  You could write your own wrapper for os.Exit, but I suspect the threading issues would be very tricky (closing resources while other goroutines are continuing to execute).  Personally, I rarely use os.Exit (in C, C++, or Go) exactly because it sidesteps unwinding the stack.  Russ' comments aside, I'd rather my C++ program spend a lot time needless releasing memory if I'm assured it is also properly closing files, terminating sockets, closing database handles, and generally being careful not to leave a mess.

(Yes, files and sockets will be closed by the OS when the program terminates.  Will it flush the buffers?  Will it ensure that the data on disk is consistent?)

Kyle Lemons

unread,
May 25, 2012, 12:12:24 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com
On Fri, May 25, 2012 at 7:48 AM, Robert Johnstone <r.w.jo...@gmail.com> wrote:
I have found that the init function for modules is pretty limited.  If anything fails, your only option is to panic.  Therefore, you typically need a separate public function for initialization.  The pattern then becomes to use defer to arrange for the termination.  i.e.

err := foo.Initialize(...)
if err!=nil { ... }
defer foo.Terminate()

This does not defend against calls to os.Exit, but I would be very upset with any libraries that called os.Exit directly.  Libraries should report an error condition, and not decide to abort the program.  You could write your own wrapper for os.Exit, but I suspect the threading issues would be very tricky (closing resources while other goroutines are continuing to execute).  Personally, I rarely use os.Exit (in C, C++, or Go) exactly because it sidesteps unwinding the stack.  Russ' comments aside, I'd rather my C++ program spend a lot time needless releasing memory if I'm assured it is also properly closing files, terminating sockets, closing database handles, and generally being careful not to leave a mess.

Even if none of your libraries call os.Exit, you still have to arrange for any panics in your own program to happen on the main goroutine in order to unwind through that defer.  This turns out to be difficult.

Robert Johnstone

unread,
May 25, 2012, 1:12:29 PM5/25/12
to golan...@googlegroups.com, Robert Johnstone
It is not that difficult to trap panics in your go routines.  These have to be dealt with accordingly, and if not, they can dumped into a channel back to your main routine.  I agree that the extra code is not fun, but it also not difficult.

At the end of the day, the OP and others in the community need a pattern or idiom to arrange for clean-up when their application terminates.  Do you have a better suggestion?



On Friday, 25 May 2012 12:12:24 UTC-4, Kyle Lemons wrote:

roger peppe

unread,
May 25, 2012, 1:18:25 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com
On 25 May 2012 18:12, Robert Johnstone <r.w.jo...@gmail.com> wrote:
> It is not that difficult to trap panics in your go routines.

indeed not. but it *is* difficult to trap panics in goroutines that
some 3rd party
package starts.

Kyle Lemons

unread,
May 25, 2012, 1:32:53 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com
On Fri, May 25, 2012 at 10:12 AM, Robert Johnstone <r.w.jo...@gmail.com> wrote:
It is not that difficult to trap panics in your go routines.  These have to be dealt with accordingly, and if not, they can dumped into a channel back to your main routine.  I agree that the extra code is not fun, but it also not difficult.

At the end of the day, the OP and others in the community need a pattern or idiom to arrange for clean-up when their application terminates.  Do you have a better suggestion?

I'm quite sympathetic to the request to have functions run on program exit.  A simple mechanism like "func fini()" to be the counterpart of "init" would be sufficient to build out all of the niceties (a function/package like atexit to register closures during program run, etc).

Ian Lance Taylor

unread,
May 25, 2012, 1:39:43 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com
Robert Johnstone <r.w.jo...@gmail.com> writes:

> At the end of the day, the OP and others in the community need a pattern or
> idiom to arrange for clean-up when their application terminates. Do you
> have a better suggestion?

The only fully reliable mechanism is a wrapper program that invokes the
real program and does the cleanup when the real program completes. That
is true in any language, not just Go.

In my somewhat unformed opinion, os.AtExit is not a great idea. It is
an unstructured facility that causes stuff to happen at program exit
time in an unpredictable order. It leads to weird scenarios like
programs that take a long time just to exit, an operation that should be
very fast. It also leads to weird functions like the C function _exit,
which more or less means exit-but-don't-run-atexit-functions.

That said, I think a special exit function corresponding to the init
function is an interesting idea. It would have the structure that
os.AtExit lacks (namely, exit functions are run in reverse order of when
init functions are run).

But exit functions won't help you if your program gets killed by the
kernel, or crashes because you call some C code that gets a segmentation
violation.

Ian

andrey mirtchovski

unread,
May 25, 2012, 1:52:55 PM5/25/12
to Ian Lance Taylor, Robert Johnstone, golan...@googlegroups.com
> It leads to weird scenarios like
> programs that take a long time just to exit, an operation that should be
> very fast.  It also leads to weird functions like the C function _exit,
> which more or less means exit-but-don't-run-atexit-functions.

It also leads to errors such as the infamous /bin/true bug where
glibc's "close_stdout", set to run atexit, will sometimes call _exit()
with a non-zero exit code overwriting /bin/true's.

Peter Bourgon

unread,
May 25, 2012, 2:03:06 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com
> At the end of the day, the OP and others in the community need a pattern or
> idiom to arrange for clean-up when their application terminates.  Do you
> have a better suggestion?

IMO, this is not a valid requirement, because it is impossible to
reliably detect the event called "application termination", as Ian
(and many others) have demonstrated. There will always be a class of
exceptional conditions that can terminate your program abruptly, and
you should design it with that in mind.

minux

unread,
May 25, 2012, 2:08:32 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com

On Fri, May 25, 2012 at 10:48 PM, Robert Johnstone <r.w.jo...@gmail.com> wrote:
snip 
Russ' comments aside, I'd rather my C++ program spend a lot time needless releasing memory if I'm assured it is also properly closing files, terminating sockets, closing database handles, and generally being careful not to leave a mess.

(Yes, files and sockets will be closed by the OS when the program terminates.  Will it flush the buffers?  Will it ensure that the data on disk is consistent?)
Personally, I prefer the style where program exit is handled exactly same as program crash.
I believe no matter how hard you try, your program can still crash under some unforeseen
situations; for example, memory shortage can bring any well-behave Go program to a crash,
and there is nothing you can do about it; so it's better to design for them.
If you follow this, you won't feel the need for atexit to clean up (because when your program
crash, atexit won't work, so you simply can't depend on it).

Chris Hines

unread,
May 25, 2012, 2:34:30 PM5/25/12
to golan...@googlegroups.com, Robert Johnstone
On Friday, May 25, 2012 1:39:43 PM UTC-4, Ian Lance Taylor wrote:
Robert Johnstone writes:

> At the end of the day, the OP and others in the community need a pattern or
> idiom to arrange for clean-up when their application terminates.  Do you
> have a better suggestion?

The only fully reliable mechanism is a wrapper program that invokes the
real program and does the cleanup when the real program completes.  That
is true in any language, not just Go.

I don't believe even that technique will handle a severed power cord or, perhaps more realistically, a hard power down.

Kyle Lemons

unread,
May 25, 2012, 3:32:07 PM5/25/12
to Chris Hines, golan...@googlegroups.com, Robert Johnstone
Just because your program can crash doesn't mean you shouldn't try to clean up after yourself and thus doesn't preclude a simple way to handle what to do when your program is exiting in some "controlled" manner.

Robert Johnstone

unread,
May 25, 2012, 3:48:26 PM5/25/12
to golan...@googlegroups.com, Robert Johnstone
The only fully reliable mechanism is a wrapper program that invokes the
real program and does the cleanup when the real program completes.  That
is true in any language, not just Go.

As others have mentioned, even that is not 100% guaranteed.  However, I'm not convinced that since a program cannot guarantee 100% that it will shut down gracefully that it should instead do nothing.

I don't know if AtExit is a good idea or not.  However, it remains that the OP has a valid complaint.  It is difficult to arrange for clean program termination in Go (one can argue if it is any better in other languages).

Chris Hines

unread,
May 25, 2012, 4:03:13 PM5/25/12
to Kyle Lemons, Chris Hines, golan...@googlegroups.com, Robert Johnstone
Indeed not, but likewise just because you make every effort to clean up doesn't mean you can rely on those efforts succeeding.

Which brings me to suggest another perspective. Sometimes it is better to cleanup the next time the program starts rather than trying to have a bulletproof means of cleaning up before it dies.

On Fri, May 25, 2012 at 3:32 PM, Kyle Lemons wrote:
Just because your program can crash doesn't mean you shouldn't try to clean up after yourself and thus doesn't preclude a simple way to handle what to do when your program is exiting in some "controlled" manner.

Michael Jones

unread,
May 25, 2012, 4:03:31 PM5/25/12
to Robert Johnstone, golan...@googlegroups.com
Much of this thread seems strangely argued to me. 

Defer won't work if the computer is hit by a meteor before the function returns. Same for an "at exit" mechanism. In fact, compiles won't complete in such cases either nor will the cooling fan rotate. Yet, we have a fan. We have a compiler. We have defer. We might want to have a main-defer that is callable outside of main, or formalize the "os at exit" library scheme.

Certainly it could be defeated by sigkill, meteors, or Godzilla's foot, yet, in the 99.99999% of the cases where there is less drama it would work perfectly. Also, one of the things one could do to defend is to write a "need to clean up from prior death" file early on, and delete it in the at-exit list, so that at start the particular library could know the score.

Not really arguing pro/con, just a meta point as a friend of the court that a claim of "it would be generally helpful" is not answered by "it could be moot in this one case."
--
Michael T. Jones | Chief Technology Advocate  | m...@google.com |  +1 650-335-5765

frederic...@gmail.com

unread,
Feb 9, 2018, 1:57:42 PM2/9/18
to golang-nuts
Here there is a solution which is cheap to use: https://gist.github.com/ZaniaDeveloper/8a5e4b804a5d7335080d7a7647f08f92


Dave Cheney

unread,
Feb 9, 2018, 3:00:29 PM2/9/18
to golang-nuts
Your program has a data race in the exitcode variable.

Richard Wilkes

unread,
Feb 9, 2018, 7:01:09 PM2/9/18
to golang-nuts
You could try something like this: https://github.com/richardwilkes/toolbox/blob/master/atexit/atexit.go

I think the discussion here has missed the primary reason I have such code in many of my programs: to cleanly shutdown when asked to terminate, either by CTRL-C or someone issuing a catchable kill command.

- Rich
Reply all
Reply to author
Forward
0 new messages