1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
|
import sys
from rpython.rlib import _rffi_stacklet as _c
from rpython.rlib import jit
from rpython.rlib.objectmodel import we_are_translated
from rpython.rtyper.lltypesystem import lltype, llmemory
DEBUG = False
class StackletThread(object):
@jit.dont_look_inside
def __init__(self, config):
self._gcrootfinder = _getgcrootfinder(config, we_are_translated())
self._thrd = _c.newthread()
if not self._thrd:
raise MemoryError
self._thrd_deleter = StackletThreadDeleter(self._thrd)
if DEBUG:
assert debug.sthread is None, "multithread debug support missing"
debug.sthread = self
@jit.dont_look_inside
def new(self, callback, arg=llmemory.NULL):
if DEBUG:
callback = _debug_wrapper(callback)
h = self._gcrootfinder.new(self, callback, arg)
if DEBUG:
debug.add(h)
return h
new._annspecialcase_ = 'specialize:arg(1)'
@jit.dont_look_inside
def switch(self, stacklet):
if DEBUG:
debug.remove(stacklet)
h = self._gcrootfinder.switch(stacklet)
if DEBUG:
debug.add(h)
return h
def is_empty_handle(self, stacklet):
# note that "being an empty handle" and being equal to
# "get_null_handle()" may be the same, or not; don't rely on it
return self._gcrootfinder.is_empty_handle(stacklet)
def get_null_handle(self):
return self._gcrootfinder.get_null_handle()
def _freeze_(self):
raise Exception("StackletThread instances must not be seen during"
" translation.")
class StackletThreadDeleter(object):
# quick hack: the __del__ is on another object, so that
# if the main StackletThread ends up in random circular
# references, on pypy deletethread() is only called
# when all that circular reference mess is gone.
def __init__(self, thrd):
self._thrd = thrd
def __del__(self):
thrd = self._thrd
if thrd:
self._thrd = lltype.nullptr(_c.thread_handle.TO)
_c.deletethread(thrd)
# ____________________________________________________________
def _getgcrootfinder(config, translated):
if translated:
assert config is not None, ("you have to pass a valid config, "
"e.g. from 'driver.config'")
elif '__pypy__' in sys.builtin_module_names:
import py
py.test.skip("cannot run the stacklet tests on top of pypy: "
"calling directly the C function stacklet_switch() "
"will crash, depending on details of your config")
if config is not None:
assert config.translation.continuation, (
"stacklet: you have to translate with --continuation")
if (config is None or
config.translation.gc in ('ref', 'boehm', 'none')): # for tests
gcrootfinder = 'n/a'
else:
gcrootfinder = config.translation.gcrootfinder
gcrootfinder = gcrootfinder.replace('/', '_')
module = __import__('rpython.rlib._stacklet_%s' % gcrootfinder,
None, None, ['__doc__'])
return module.gcrootfinder
_getgcrootfinder._annspecialcase_ = 'specialize:memo'
class StackletDebugError(Exception):
pass
class Debug(object):
def __init__(self):
self.sthread = None
self.active = []
def _cleanup_(self):
self.__init__()
def add(self, h):
if not self.sthread.is_empty_handle(h):
if h == self.sthread.get_null_handle():
raise StackletDebugError("unexpected null handle")
self.active.append(h)
def remove(self, h):
try:
i = self.active.index(h)
except ValueError:
if self.sthread.is_empty_handle(h):
msg = "empty stacklet handle"
elif h == self.sthread.get_null_handle():
msg = "unexpected null handle"
else:
msg = "double usage of handle %r" % (h,)
raise StackletDebugError(msg)
del self.active[i]
debug = Debug()
def _debug_wrapper(callback):
def wrapper(h, arg):
debug.add(h)
h = callback(h, arg)
debug.remove(h)
return h
return wrapper
_debug_wrapper._annspecialcase_ = 'specialize:memo'
|