[Zope] [Warning] Zope/ZEO clients: subprocesses can lead to non-deterministic message loss

Dieter Maurer dieter at handshake.de
Fri Jun 25 13:23:19 EDT 2004


ATTENTION: Crosspost -- Reply-To set to 'zope-dev at zope.org'

Today, I hit a nasty error.

The error affects applications under Unix (and maybe Windows) which

  *  use an "asyncore" mainloop thread (and maybe other asyncore applications)

     Zope and many ZEO clients belong to this class

and

  *  create subprocesses (via "fork" and "system", "popen" or friends if
     they use "fork" internally (they do under Unix but I think not
     under Windows)).

The error can cause non-deterministic loss
of messages (HTTP requests, ZEO server responses, ...) destined
for the parent process.
It also can cause the same output to be send several times over sockets.


The error is explained as follows:

  "asyncore" maintains a map from file descriptors to handlers.
  The "asyncore" main loop waits for any file descriptor to
  become "active" and then calls the corresponding handler.

  When a process forks the complete state, including file descriptors,
  threads and memory state is copied and the new process
  executes in this copied state.
  We now have 2 "asyncore" threads waiting for the same events.

  File descriptors are shared between parent and child.
  When the child reads from a file descriptor from its parent,
  it steals the corresponding message: the message will
  not reach the parent.

  While file descriptors are shared, memory state is separate.
  Therefore, pending writes can be performed by both
  parent and child -- leading to duplicate writes to the same
  file descriptor.


A workaround it to deactivate "asyncore" before forking
(or "system", "popen", ...) and reactivate it afterwards: as exemplified
in the following code:

     from asyncore import socket_map
     saved_socket_map = socket_map.copy()
     socket_map.clear() # deactivate "asyncore"
     pid = None
     try:
         pid = fork()
	 if (pid == 0):
	     # child
	     # ...
     finally:
         if pid != 0:
	     socket_map.update(saved_socket_map) # reactivate "asyncore"


-- 
Dieter


More information about the Zope mailing list