[Python-Dev] Threading, atexit, and logging

Tim Peters tim.peters at gmail.com
Wed Dec 6 09:47:26 CET 2006


[Martin v. Löwis]
> In bug #1566280 somebody reported that he gets an
> exception where the logging module tries to write
> to closed file descriptor.
>
> Upon investigation, it turns out that the file descriptor
> is closed because the logging atexit handler is invoked.
> This is surprising, as the program is far from exiting at
> this point.

But the main thread is done, right?  None of this appears to make
sense unless we got into Py_Finalize(), and that doesn't happen until
the main thread has nothing left to do.

> Investigating further, I found that the logging atexit
> handler is registered *after* the threading atexit handler,
> so it gets invoked *before* the threading's atexit.

Ya, and that sucks.  Can't recall details now, but it's not the first
time the vagaries of atexit ordering bit a threaded program.  IMO,
`threading` shouldn't use atexit at all.

> Now, threading's atexit is the one that keeps the
> application running, by waiting for all non-daemon threads
> to shut down. As this application does all its work in
> non-daemon threads, it keeps running for quite a while -
> except that the logging module gives errors.
>
> The real problem here is that atexit handlers are
> invoked even though the program logically doesn't exit,
> yet (it's not just that the threading atexit is invoked
> after logging atexit - this could happen to any atexit
> handler that gets registered).
>
> I added a patch to this report which makes the MainThread
> __exitfunc a sys.exitfunc, chaining what is there already.
> This will work fine for atexit (as atexit is still imported
> explicitly to register its sys.exitfunc), but it might break
> if other applications still insist on installing a
> sys.exitfunc.

Well, that's been officially deprecated since 2.4, but who knows?

> What do you think about this approach?

It's expedient :-)  So was using atexit for this to begin with.
Probably "good enough".  I'd rather, e.g., that `threading` stuff an
exit function into a module global, and change Py_Finalize() to look
for that and run it (if present) before invoking call_sys_exitfunc().
That is, break all connection between the core's implementation of
threading and the user-visible `atexit` machinery.

`atexit` is a hack specific to "don't care about order" finalization
functions, and it gets increasingly obscure to try to force it to
respect a specific ordering sometimes (e.g., now you have a patch to
try to fix it by relying on an obscure deprecated feature and hoping
users don't screw with that too -- probably "good enough", but still
sucky).


More information about the Python-Dev mailing list