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]

Reply via email to