OK, here is another interesting problem which took several hours to
track. When a Gtk program forks off, it inherits the X file
descriptors from its parent. It seems that when I call sys.exit(),
Gdk gains control (either through atexit() or through a PyGtk cleanup
intercepting SystemExit), interacts with the X file descriptors,
totally hosing the parent. As a result, the parent's Gdk reports IO
errors and aborts.
The typical code where this problem bites looks like this:
pid = os.fork ()
if pid == 0:
# Child
try:
os.execlp ("foo", "bar", "baz")
except:
# An error occurred during execlp; exit immediately
sys.exit (127)
In a Gtk program, if the execlp raises an exception the *parent* will
coredump. If you replace `sys.exit(127)' with
`os.kill(getpid(),signal.SIGTERM)', no coredump happens because the
Gdk stuff never gets invoked.
Fixes/workarounds include, in the increasing order of usefulness:
1) The child closing all the file descriptors except the ones you're
interested in. This is a gross hack that looks like this:
for fd in range(32):
if not fd in (0, 1, 2, self.writefd):
try:
os.close (fd)
except:
pass # Ignore EBADF
2) The child closing only the X file descriptor. This would require
Gdk to somehow export the descriptors it uses, so that Python child
knows what to close. This is also a gross hack, but faster than
the above.
3) Making sure that random Gdk code isn't called at exit time.
4) Providing a function that cleanly shuts down Gtk/Gdk, *without*
messing with the descriptors (except for perhaps closing them), and
*without* calling any callbacks. This, cleanest solution, would
also assume that #3 is being done, for programs that don't bother
to call the function.
The test program is appended below. It forks off and exits the child
after a second's sleep. For me, the parent then coredumps with this
message:
Gdk-ERROR **: an x io error occurred
aborting...
If you uncomment the os.kill(...) statement, it works as expected.
#!/usr/local/bin/python
from gtk import *
import os, sys, signal, time
def fork_off ():
pid = os.fork ()
if pid == 0:
# Child
time.sleep (1)
#os.kill (os.getpid (), signal.SIGTERM)
sys.exit (127)
else:
# Parent
os.wait ()
return FALSE
w = GtkWindow ()
w.connect ("destroy", mainquit)
w.show_all ()
# Call fork_off() as soon as mainloop() starts.
timeout_add (1, lambda *args: fork_off ())
mainloop ()
To unsubscribe: echo "unsubscribe" | mail [EMAIL PROTECTED]