[Python-Dev] pthreads, fork, import, and execvp
Nick Coghlan
ncoghlan at gmail.com
Tue May 16 11:28:39 CEST 2006
Martin v. Löwis wrote:
> So if the the import lock was held during the fork call, the new thread
> will hold the import lock of the new process, and subsequent imports
> will block.
> However, then the problem is not with the execve implementation, but
> with the fact that the import lock was held when the process forked.
>
> Rotem should simply avoid to fork() in the toplevel code of a module.
That's what I originally thought, but Rotem was able to clarify for me that
the problem occurs even if the import lock is held by a *different* thread at
the time fork() is called. So a deadlock can be triggered by perfectly
legitimate code that has the misfortune to fork the process while another
thread is doing a module import.
> Unfortunately, that fix would apply to AIX only, though:
>
> void
> _PyImport_ReInitLock(void)
> {
> #ifdef _AIX
> if (import_lock != NULL)
> import_lock = PyThread_allocate_lock();
> #endif
> }
>
> If we want to support fork while an import is going on, we should
> release the import lock if it is held. Alternatively, the code
> could throw away the old import lock on all systems; that seems
> like a waste of resources to me, though. One should also reset
> import_lock_thread and import_lock_level.
Broadening the ifdef to cover all posix systems rather than just AIX might be
the right thing to do. However, the first thing would be to reproduce the
deadlock reliably.
A repeatable test case would look something like this:
class ForkingThread(Thread):
def run(self):
# create a subprocess using subprocess.Popen
# and ensure it tries to do an import operation
# (i.e. something similar to what Rotem & Yair's code is doing)
class ImportingThread(Thread):
def __init__(self):
Thread.__init__(self)
self.importing = Event()
self.cleanup = Event()
def run(self):
imp.acquire_lock()
try:
self.importing.set()
self.cleanup.wait()
finally:
imp.release_lock()
def test_forked_import_deadlock():
import_thread = ImportingThread()
fork_thread = ForkingThread()
import_thread.start()
try:
import_thread.importing.wait()
fork_thread.start() # Fork while the import thread holds the lock
fork_thread.join(10) # Give the subprocess time to finish
assert not fork_thread.isAlive() # A timeout means it deadlocked
finally:
import_thread.cleanup.set() # Release the import lock
Cheers,
Nick.
--
Nick Coghlan | ncoghlan at gmail.com | Brisbane, Australia
---------------------------------------------------------------
http://www.boredomandlaziness.org
More information about the Python-Dev
mailing list