Author: Armin Rigo <[email protected]>
Branch: ec-threadlocal
Changeset: r72132:22aa4ef3e414
Date: 2014-06-22 17:06 +0200
http://bitbucket.org/pypy/pypy/changeset/22aa4ef3e414/

Log:    in-progress

diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -395,6 +395,7 @@
 
     def startup(self):
         # To be called before using the space
+        self.threadlocals.enter_thread(self)
 
         # Initialize already imported builtin modules
         from pypy.interpreter.module import Module
@@ -639,30 +640,33 @@
         """NOT_RPYTHON: Abstract method that should put some minimal
         content into the w_builtins."""
 
-    @jit.loop_invariant
     def getexecutioncontext(self):
         "Return what we consider to be the active execution context."
         # Important: the annotator must not see a prebuilt ExecutionContext:
         # you should not see frames while you translate
         # so we make sure that the threadlocals never *have* an
         # ExecutionContext during translation.
-        if self.config.translating and not we_are_translated():
-            assert self.threadlocals.getvalue() is None, (
-                "threadlocals got an ExecutionContext during translation!")
-            try:
-                return self._ec_during_translation
-            except AttributeError:
-                ec = self.createexecutioncontext()
-                self._ec_during_translation = ec
+        if not we_are_translated():
+            if self.config.translating:
+                assert self.threadlocals.get_ec() is None, (
+                    "threadlocals got an ExecutionContext during translation!")
+                try:
+                    return self._ec_during_translation
+                except AttributeError:
+                    ec = self.createexecutioncontext()
+                    self._ec_during_translation = ec
+                    return ec
+            else:
+                ec = self.threadlocals.get_ec()
+                if ec is None:
+                    self.threadlocals.enter_thread(self)
+                    ec = self.threadlocals.get_ec()
                 return ec
-        # normal case follows.  The 'thread' module installs a real
-        # thread-local object in self.threadlocals, so this builds
-        # and caches a new ec in each thread.
-        ec = self.threadlocals.getvalue()
-        if ec is None:
-            ec = self.createexecutioncontext()
-            self.threadlocals.setvalue(ec)
-        return ec
+        else:
+            # translated case follows.  self.threadlocals is either from
+            # 'pypy.interpreter.miscutils' or 
'pypy.module.thread.threadlocals'.
+            # the result is assumed to be non-null: enter_thread() was called.
+            return self.threadlocals.get_ec()
 
     def _freeze_(self):
         return True
diff --git a/pypy/interpreter/miscutils.py b/pypy/interpreter/miscutils.py
--- a/pypy/interpreter/miscutils.py
+++ b/pypy/interpreter/miscutils.py
@@ -11,11 +11,11 @@
     """
     _value = None
 
-    def getvalue(self):
+    def get_ec(self):
         return self._value
 
-    def setvalue(self, value):
-        self._value = value
+    def enter_thread(self, space):
+        self._value = space.createexecutioncontext()
 
     def signals_enabled(self):
         return True
diff --git a/pypy/module/thread/__init__.py b/pypy/module/thread/__init__.py
--- a/pypy/module/thread/__init__.py
+++ b/pypy/module/thread/__init__.py
@@ -26,10 +26,11 @@
         "NOT_RPYTHON: patches space.threadlocals to use real threadlocals"
         from pypy.module.thread import gil
         MixedModule.__init__(self, space, *args)
-        prev = space.threadlocals.getvalue()
+        prev_ec = space.threadlocals.get_ec()
         space.threadlocals = gil.GILThreadLocals()
         space.threadlocals.initialize(space)
-        space.threadlocals.setvalue(prev)
+        if prev_ec is not None:
+            space.threadlocals._set_ec(prev_ec)
 
         from pypy.module.posix.interp_posix import add_fork_hook
         from pypy.module.thread.os_thread import reinit_threads
diff --git a/pypy/module/thread/os_thread.py b/pypy/module/thread/os_thread.py
--- a/pypy/module/thread/os_thread.py
+++ b/pypy/module/thread/os_thread.py
@@ -126,6 +126,8 @@
     release = staticmethod(release)
 
     def run(space, w_callable, args):
+        # add the ExecutionContext to space.threadlocals
+        space.threadlocals.enter_thread(space)
         try:
             space.call_args(w_callable, args)
         except OperationError, e:
diff --git a/pypy/module/thread/threadlocals.py 
b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -1,10 +1,13 @@
 from rpython.rlib import rthread
+from rpython.rlib.objectmodel import we_are_translated
 from pypy.module.thread.error import wrap_thread_error
 from pypy.interpreter.executioncontext import ExecutionContext
 
 
 ExecutionContext._signals_enabled = 0     # default value
 
+raw_thread_local = rthread.ThreadLocalReference(ExecutionContext)
+
 
 class OSThreadLocals:
     """Thread-local storage for OS-level threads.
@@ -19,47 +22,54 @@
     def _cleanup_(self):
         self._valuedict.clear()
         self._mainthreadident = 0
-        self._mostrecentkey = 0        # fast minicaching for the common case
-        self._mostrecentvalue = None   # fast minicaching for the common case
 
-    def getvalue(self):
+    def enter_thread(self, space):
+        "Notification that the current thread is about to start running."
+        self._set_ec(space.createexecutioncontext())
+
+    def _set_ec(self, ec):
         ident = rthread.get_ident()
-        if ident == self._mostrecentkey:
-            result = self._mostrecentvalue
-        else:
-            value = self._valuedict.get(ident, None)
-            # slow path: update the minicache
-            self._mostrecentkey = ident
-            self._mostrecentvalue = value
-            result = value
-        return result
+        if self._mainthreadident == 0 or self._mainthreadident == ident:
+            ec._signals_enabled = 1    # the main thread is enabled
+            self._mainthreadident = ident
+        self._valuedict[ident] = ec
+        # This logic relies on hacks and _make_sure_does_not_move().
+        # It only works because we keep the 'ec' alive in '_valuedict' too.
+        raw_thread_local.set(ec)
 
-    def setvalue(self, value):
-        ident = rthread.get_ident()
-        if value is not None:
-            if self._mainthreadident == 0:
-                value._signals_enabled = 1    # the main thread is enabled
-                self._mainthreadident = ident
-            self._valuedict[ident] = value
-        else:
+    def leave_thread(self, space):
+        "Notification that the current thread is about to stop."
+        from pypy.module.thread.os_local import thread_is_stopping
+        ec = self.get_ec()
+        if ec is not None:
             try:
-                del self._valuedict[ident]
-            except KeyError:
-                pass
-        # update the minicache to prevent it from containing an outdated value
-        self._mostrecentkey = ident
-        self._mostrecentvalue = value
+                thread_is_stopping(ec)
+            finally:
+                raw_thread_local.set(None)
+                ident = rthread.get_ident()
+                try:
+                    del self._valuedict[ident]
+                except KeyError:
+                    pass
+
+    def get_ec(self):
+        ec = raw_thread_local.get()
+        if not we_are_translated():
+            assert ec is self._valuedict.get(rthread.get_ident(), None)
+        return ec
 
     def signals_enabled(self):
-        ec = self.getvalue()
+        ec = self.get_ec()
         return ec is not None and ec._signals_enabled
 
     def enable_signals(self, space):
-        ec = self.getvalue()
+        ec = self.get_ec()
+        assert ec is not None
         ec._signals_enabled += 1
 
     def disable_signals(self, space):
-        ec = self.getvalue()
+        ec = self.get_ec()
+        assert ec is not None
         new = ec._signals_enabled - 1
         if new < 0:
             raise wrap_thread_error(space,
@@ -69,22 +79,13 @@
     def getallvalues(self):
         return self._valuedict
 
-    def leave_thread(self, space):
-        "Notification that the current thread is about to stop."
-        from pypy.module.thread.os_local import thread_is_stopping
-        ec = self.getvalue()
-        if ec is not None:
-            try:
-                thread_is_stopping(ec)
-            finally:
-                self.setvalue(None)
-
     def reinit_threads(self, space):
         "Called in the child process after a fork()"
         ident = rthread.get_ident()
-        ec = self.getvalue()
+        ec = self.get_ec()
+        assert ec is not None
         if ident != self._mainthreadident:
             ec._signals_enabled += 1
         self._cleanup_()
         self._mainthreadident = ident
-        self.setvalue(ec)
+        self._set_ec(ec)
diff --git a/rpython/rlib/rthread.py b/rpython/rlib/rthread.py
--- a/rpython/rlib/rthread.py
+++ b/rpython/rlib/rthread.py
@@ -272,3 +272,43 @@
         llop.gc_thread_after_fork(lltype.Void, result_of_fork, opaqueaddr)
     else:
         assert opaqueaddr == llmemory.NULL
+
+# ____________________________________________________________
+#
+# Thread-locals.  Only for references that are not changed often.
+# KEEP THE REFERENCE ALIVE, THE GC DOES NOT FOLLOW THEM SO FAR!
+# We use _make_sure_does_not_move() to make sure the pointer will not move.
+
+class ThreadLocalReference(object):
+    _COUNT = 1
+
+    def __init__(self, Cls):
+        "NOT_RPYTHON: must be prebuilt"
+        import thread
+        self.Cls = Cls
+        self.local = thread._local()      # <- NOT_RPYTHON
+        self.unique_id = ThreadLocalReference._COUNT
+        ThreadLocalReference._COUNT += 1
+
+    def _freeze_(self):
+        return True
+
+    @specialize.arg(0)
+    def get(self):
+        if we_are_translated():
+            from rpython.rtyper.lltypesystem import rclass
+            from rpython.rtyper.annlowlevel import cast_base_ptr_to_instance
+            ptr = llop.threadlocalref_get(rclass.OBJECTPTR, self.unique_id)
+            return cast_base_ptr_to_instance(self.Cls, ptr)
+        else:
+            return getattr(self.local, 'value', None)
+
+    @specialize.arg(0)
+    def set(self, value):
+        assert isinstance(value, self.Cls) or value is None
+        if we_are_translated():
+            from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr
+            ptr = cast_instance_to_base_ptr(value)
+            llop.threadlocalref_set(lltype.Void, self.unique_id, ptr)
+        else:
+            self.local.value = value
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to