def setup_class(cls): from test_basic import compile, run class EC(object): def __init__(self, value): self.value = value raw_thread_local = rthread.ThreadLocalReference(EC) def bootstrap(): rthread.gc_thread_start() _sleep(1) ec = EC(4567) raw_thread_local.set(ec) revdb.stop_point() print raw_thread_local.get().value assert raw_thread_local.get() is ec rthread.gc_thread_die() def main(argv): revdb.stop_point() ec = EC(12) raw_thread_local.set(ec) rthread.start_new_thread(bootstrap, ()) _sleep(2) print raw_thread_local.get().value assert raw_thread_local.get() is ec return 9 compile(cls, main, backendopt=False, thread=True) assert run(cls, '') == '4567\n12\n'
def test_threadlocal(self): class EC(object): def __init__(self, value): self.value = value raw_thread_local = rthread.ThreadLocalReference(EC) def bootstrap(): rthread.gc_thread_start() _sleep(1) ec = EC(4567) raw_thread_local.set(ec) print raw_thread_local.get().value assert raw_thread_local.get() is ec rthread.gc_thread_die() def main(argv): ec = EC(12) raw_thread_local.set(ec) rthread.start_new_thread(bootstrap, ()) _sleep(2) print raw_thread_local.get().value assert raw_thread_local.get() is ec return 9 self.compile(main, backendopt=False, thread=True) out = self.run('Xx') # should have printed 4567 and 12 rdb = self.fetch_rdb([self.exename, 'Xx']) th_A = rdb.main_thread_id rdb.same_stack() # RPyGilAllocate() th_B = self.start_thread_B(rdb, th_A) rdb.gil_acquire() rdb.gil_release() rdb.switch_thread(th_B) rdb.same_stack() # sleep() (finishes here) rdb.next('i') # sleep() rdb.gil_acquire() rdb.write_call("4567\n") rdb.gil_release() rdb.switch_thread(th_A) rdb.same_stack() # sleep() rdb.next('i') # sleep() rdb.gil_acquire() rdb.write_call("12\n") rdb.done()
def __init__(self, space): "NOT_RPYTHON" # # This object tracks code that enters and leaves threads. # There are two APIs. For Python-level threads, we know when # the thread starts and ends, and we call enter_thread() and # leave_thread(). In a few other cases, like callbacks, we # might be running in some never-seen-before thread: in this # case, the callback logic needs to call try_enter_thread() at # the start, and if this returns True it needs to call # leave_thread() at the end. # # We implement an optimization for the second case (which only # works if we translate with a framework GC and with # rweakref). If try_enter_thread() is called in a # never-seen-before thread, it still returns False and # remembers the ExecutionContext with 'self._weaklist'. The # next time we call try_enter_thread() again in the same # thread, the ExecutionContext is reused. The optimization is # not completely invisible to the user: '******' # values will remain. We can argue that it is the correct # behavior to do that, and the behavior we get if the # optimization is disabled is buggy (but hard to do better # then). # # 'self._valuedict' is a dict mapping the thread idents to # ExecutionContexts; it does not list the ExecutionContexts # which are in 'self._weaklist'. (The latter is more precisely # a list of AutoFreeECWrapper objects, defined below, which # each references the ExecutionContext.) # self.space = space self._valuedict = {} self._cleanup_() self.raw_thread_local = rthread.ThreadLocalReference( ExecutionContext, loop_invariant=True)
def __init__(self): "NOT_RPYTHON" self._valuedict = {} # {thread_ident: ExecutionContext()} self._cleanup_() self.raw_thread_local = rthread.ThreadLocalReference( ExecutionContext, loop_invariant=True)