[pypy-commit] pypy default: Issue 1895: test and fix for the file's locks on multithreaded apps with fork()
arigo
noreply at buildbot.pypy.org
Mon Nov 3 15:29:57 CET 2014
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r74331:99a70ef5eaaf
Date: 2014-11-03 15:29 +0100
http://bitbucket.org/pypy/pypy/changeset/99a70ef5eaaf/
Log: Issue 1895: test and fix for the file's locks on multithreaded apps
with fork()
diff --git a/pypy/interpreter/executioncontext.py b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -32,6 +32,17 @@
self.compiler = space.createcompiler()
self.profilefunc = None
self.w_profilefuncarg = None
+ self.thread_disappeared = False # might be set to True after os.fork()
+
+ @staticmethod
+ def _mark_thread_disappeared(space):
+ # Called in the child process after os.fork() by interp_posix.py.
+ # Marks all ExecutionContexts except the current one
+ # with 'thread_disappeared = True'.
+ me = space.getexecutioncontext()
+ for ec in space.threadlocals.getallvalues().values():
+ if ec is not me:
+ ec.thread_disappeared = True
def gettopframe(self):
return self.topframeref()
diff --git a/pypy/module/_file/interp_stream.py b/pypy/module/_file/interp_stream.py
--- a/pypy/module/_file/interp_stream.py
+++ b/pypy/module/_file/interp_stream.py
@@ -34,8 +34,12 @@
# this function runs with the GIL acquired so there is no race
# condition in the creation of the lock
me = self.space.getexecutioncontext() # used as thread ident
- if self.slockowner is me:
- return False # already acquired by the current thread
+ if self.slockowner is not None:
+ if self.slockowner is me:
+ return False # already acquired by the current thread
+ if self.slockowner.thread_disappeared:
+ self.slockowner = None
+ self.slock = None
try:
if self.slock is None:
self.slock = self.space.allocate_lock()
diff --git a/pypy/module/posix/interp_posix.py b/pypy/module/posix/interp_posix.py
--- a/pypy/module/posix/interp_posix.py
+++ b/pypy/module/posix/interp_posix.py
@@ -10,6 +10,7 @@
from pypy.interpreter.gateway import unwrap_spec
from pypy.interpreter.error import OperationError, wrap_oserror, wrap_oserror2
+from pypy.interpreter.executioncontext import ExecutionContext
from pypy.module.sys.interp_encoding import getfilesystemencoding
@@ -721,6 +722,8 @@
"NOT_RPYTHON"
get_fork_hooks(where).append(hook)
+add_fork_hook('child', ExecutionContext._mark_thread_disappeared)
+
@specialize.arg(0)
def run_fork_hooks(where, space):
for hook in get_fork_hooks(where):
diff --git a/pypy/module/test_lib_pypy/test_posix_extra.py b/pypy/module/test_lib_pypy/test_posix_extra.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/test_lib_pypy/test_posix_extra.py
@@ -0,0 +1,40 @@
+import py
+import sys, os, subprocess
+
+
+CODE = """
+import sys, os, thread, time
+
+fd1, fd2 = os.pipe()
+f1 = os.fdopen(fd1, 'r', 0)
+f2 = os.fdopen(fd2, 'w', 0)
+
+def f():
+ print "thread started"
+ x = f1.read(1)
+ assert x == "X"
+ print "thread exit"
+
+thread.start_new_thread(f, ())
+time.sleep(0.5)
+if os.fork() == 0: # in the child
+ time.sleep(0.5)
+ x = f1.read(1)
+ assert x == "Y"
+ print "ok!"
+ sys.exit()
+
+f2.write("X") # in the parent
+f2.write("Y") # in the parent
+time.sleep(1.0)
+"""
+
+
+def test_thread_fork_file_lock():
+ if not hasattr(os, 'fork'):
+ py.test.skip("requires 'fork'")
+ output = subprocess.check_output([sys.executable, '-u', '-c', CODE])
+ assert output.splitlines() == [
+ 'thread started',
+ 'thread exit',
+ 'ok!']
More information about the pypy-commit
mailing list