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