[pypy-svn] r46639 - in pypy/dist/pypy: lib module/__builtin__ module/thread module/thread/test

arigo at codespeak.net arigo at codespeak.net
Sat Sep 15 15:39:55 CEST 2007


Author: arigo
Date: Sat Sep 15 15:39:55 2007
New Revision: 46639

Modified:
   pypy/dist/pypy/lib/imp.py
   pypy/dist/pypy/module/__builtin__/importing.py
   pypy/dist/pypy/module/thread/__init__.py
   pypy/dist/pypy/module/thread/os_thread.py
   pypy/dist/pypy/module/thread/test/test_import_lock.py
Log:
Move the import lock at interp-level.  This is less of a hack,
and it also has the advantage that it can be slightly finer-grained.
Something easy to satisfy like 'import sys' should not block even if
another thread has acquired the import lock.


Modified: pypy/dist/pypy/lib/imp.py
==============================================================================
--- pypy/dist/pypy/lib/imp.py	(original)
+++ pypy/dist/pypy/lib/imp.py	Sat Sep 15 15:39:55 2007
@@ -140,73 +140,3 @@
 
 def is_frozen(name):
     return False
-
-
-# ____________________________________________________________
-
-try:
-    # PyPy-specific interface: hint from the thread module to ask us to
-    # provide an import lock
-    from thread import _please_provide_import_lock
-except ImportError:
-    def lock_held():
-        """On platforms without threads, return False."""
-        return False
-    def acquire_lock():
-        """On platforms without threads, this function does nothing."""
-    def release_lock():
-        """On platforms without threads, this function does nothing."""
-
-else:
-    del _please_provide_import_lock
-    import thread
-
-    class _ImportLock:
-        def __init__(self):
-            self.lock = thread.allocate_lock()
-            self.in_thread = None
-            self.recursions = 0
-
-        def held(self):
-            """Return True if the import lock is currently held, else False."""
-            return self.in_thread is not None
-
-        def acquire(self):
-            """Acquires the interpreter's import lock for the current thread.
-            This lock should be used by import hooks to ensure thread-safety
-            when importing modules.
-            """
-            myident = thread.get_ident()
-            if self.in_thread == myident:
-                self.recursions += 1
-            else:
-                self.lock.acquire()
-                self.in_thread = myident
-                self.recursions = 1
-
-        def release(self):
-            """Release the interpreter's import lock."""
-            myident = thread.get_ident()
-            if self.in_thread != myident:
-                raise RuntimeError("not holding the import lock")
-            self.recursions -= 1
-            if self.recursions == 0:
-                self.in_thread = None
-                self.lock.release()
-
-    _importlock = _ImportLock()
-
-    lock_held    = _importlock.held
-    acquire_lock = _importlock.acquire
-    release_lock = _importlock.release
-
-    import __builtin__
-    _original_import_hook = __builtin__.__import__
-    def __lockedimport__(modulename, globals=None, locals=None, fromlist=None):
-        acquire_lock()
-        try:
-            return _original_import_hook(modulename, globals, locals, fromlist)
-        finally:
-            release_lock()
-    __builtin__.__import__ = __lockedimport__
-    del __builtin__

Modified: pypy/dist/pypy/module/__builtin__/importing.py
==============================================================================
--- pypy/dist/pypy/module/__builtin__/importing.py	(original)
+++ pypy/dist/pypy/module/__builtin__/importing.py	Sat Sep 15 15:39:55 2007
@@ -196,6 +196,16 @@
 importhook.unwrap_spec = [ObjSpace,str,W_Root,W_Root,W_Root]
 
 def absolute_import(space, modulename, baselevel, w_fromlist, tentative):
+    lock = getimportlock(space)
+    acquired = lock.try_acquire_lock()
+    try:
+        return _absolute_import(space, modulename, baselevel,
+                                w_fromlist, tentative)
+    finally:
+        if acquired:
+            lock.release_lock()
+
+def _absolute_import(space, modulename, baselevel, w_fromlist, tentative):
     w = space.wrap
     
     w_mod = None
@@ -286,6 +296,45 @@
 
 # __________________________________________________________________
 #
+# import lock, to prevent two threads from running module-level code in
+# parallel.  This behavior is more or less part of the language specs,
+# as an attempt to avoid failure of 'from x import y' if module x is
+# still being executed in another thread.
+
+class ImportRLock:
+    def __init__(self, space):
+        self.space = space
+        self.lock = None
+        self.lockowner = None
+
+    def _freeze_(self):
+        # remove the lock object, which will be created again as need at
+        # run-time.
+        self.lock = None
+        assert self.lockowner is None
+        return False
+
+    def try_acquire_lock(self):
+        # this function runs with the GIL acquired so there is no race
+        # condition in the creation of the lock
+        if self.lock is None:
+            self.lock = self.space.allocate_lock()
+        me = self.space.getexecutioncontext()   # used as thread ident
+        if self.lockowner is me:
+            return False    # already acquired by the current thread
+        self.lock.acquire(True)
+        self.lockowner = me
+        return True
+
+    def release_lock(self):
+        self.lockowner = None
+        self.lock.release()
+
+def getimportlock(space):
+    return space.fromcache(ImportRLock)
+
+# __________________________________________________________________
+#
 # .pyc file support
 
 """

Modified: pypy/dist/pypy/module/thread/__init__.py
==============================================================================
--- pypy/dist/pypy/module/thread/__init__.py	(original)
+++ pypy/dist/pypy/module/thread/__init__.py	Sat Sep 15 15:39:55 2007
@@ -17,7 +17,6 @@
         'allocate':               'os_lock.allocate_lock',  # obsolete synonym
         'LockType':               'os_lock.getlocktype(space)',
         '_local':                 'os_local.getlocaltype(space)',
-        '_please_provide_import_lock': '(space.w_True)',   # for imp.py
     }
 
     def __init__(self, space, *args):

Modified: pypy/dist/pypy/module/thread/os_thread.py
==============================================================================
--- pypy/dist/pypy/module/thread/os_thread.py	(original)
+++ pypy/dist/pypy/module/thread/os_thread.py	Sat Sep 15 15:39:55 2007
@@ -52,11 +52,7 @@
 
 
 def setup_threads(space):
-    if space.threadlocals.setup_threads(space):
-        # the import lock is in imp.py, which registers a custom import
-        # hook with this lock.
-        from pypy.module.__builtin__.importing import importhook
-        importhook(space, 'imp')
+    space.threadlocals.setup_threads(space)
 
 
 def start_new_thread(space, w_callable, w_args, w_kwargs=NoneNotWrapped):

Modified: pypy/dist/pypy/module/thread/test/test_import_lock.py
==============================================================================
--- pypy/dist/pypy/module/thread/test/test_import_lock.py	(original)
+++ pypy/dist/pypy/module/thread/test/test_import_lock.py	Sat Sep 15 15:39:55 2007
@@ -13,3 +13,7 @@
             thread.start_new_thread(f, ())
         self.waitfor(lambda: len(done) == 5)
         assert len(done) == 5
+
+    def test_with_many_dependencies(self):
+        import thread
+        import re      # -> causes nested imports



More information about the Pypy-commit mailing list