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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
import py
from rpython.translator import cdir
from rpython.translator.tool.cbuild import ExternalCompilationInfo
from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
from rpython.rtyper.extregistry import ExtRegistryEntry
# these functions manipulate directly the GIL, whose definition does not
# escape the C code itself
translator_c_dir = py.path.local(cdir)
eci = ExternalCompilationInfo(
includes = ['src/thread.h'],
separate_module_files = [translator_c_dir / 'src' / 'thread.c'],
include_dirs = [translator_c_dir],
post_include_bits = ['#define RPY_WITH_GIL'])
llexternal = rffi.llexternal
_gil_allocate = llexternal('RPyGilAllocate', [], lltype.Void,
_nowrapper=True, sandboxsafe=True,
compilation_info=eci)
_gil_yield_thread = llexternal('RPyGilYieldThread', [], lltype.Signed,
_nowrapper=True, sandboxsafe=True,
compilation_info=eci)
_gil_release = llexternal('RPyGilRelease', [], lltype.Void,
_nowrapper=True, sandboxsafe=True,
compilation_info=eci)
_gil_acquire = llexternal('RPyGilAcquire', [], lltype.Void,
_nowrapper=True, sandboxsafe=True,
compilation_info=eci)
gil_fetch_fastgil = llexternal('RPyFetchFastGil', [], llmemory.Address,
_nowrapper=True, sandboxsafe=True,
compilation_info=eci)
# ____________________________________________________________
def invoke_after_thread_switch(callback):
"""Invoke callback() after a thread switch.
This is a hook used by pypy.module.signal. Several callbacks should
be easy to support (but not right now).
This function should be called from the translated RPython program
(i.e. *not* at module level!), but registers the callback
statically. The exact point at which invoke_after_thread_switch()
is called has no importance: the callback() will be called anyway.
"""
print "NOTE: invoke_after_thread_switch() is meant to be translated "
print "and not called directly. Using some emulation."
global _emulated_after_thread_switch
_emulated_after_thread_switch = callback
_emulated_after_thread_switch = None
def _after_thread_switch():
"""NOT_RPYTHON"""
if _emulated_after_thread_switch is not None:
_emulated_after_thread_switch()
class Entry(ExtRegistryEntry):
_about_ = invoke_after_thread_switch
def compute_result_annotation(self, s_callback):
assert s_callback.is_constant()
callback = s_callback.const
bk = self.bookkeeper
translator = bk.annotator.translator
if hasattr(translator, '_rgil_invoke_after_thread_switch'):
assert translator._rgil_invoke_after_thread_switch == callback, (
"not implemented yet: several invoke_after_thread_switch()")
else:
translator._rgil_invoke_after_thread_switch = callback
bk.emulate_pbc_call("rgil.invoke_after_thread_switch", s_callback, [])
def specialize_call(self, hop):
# the actual call is not done here
hop.exception_cannot_occur()
class Entry(ExtRegistryEntry):
_about_ = _after_thread_switch
def compute_result_annotation(self):
# the call has been emulated already in invoke_after_thread_switch()
pass
def specialize_call(self, hop):
translator = hop.rtyper.annotator.translator
if hasattr(translator, '_rgil_invoke_after_thread_switch'):
func = translator._rgil_invoke_after_thread_switch
graph = translator._graphof(func)
llfn = hop.rtyper.getcallable(graph)
c_callback = hop.inputconst(lltype.typeOf(llfn), llfn)
hop.exception_is_here()
hop.genop("direct_call", [c_callback])
else:
hop.exception_cannot_occur()
def allocate():
_gil_allocate()
def release():
# this function must not raise, in such a way that the exception
# transformer knows that it cannot raise!
_gil_release()
release._gctransformer_hint_cannot_collect_ = True
release._dont_reach_me_in_del_ = True
def acquire():
from rpython.rlib import rthread
_gil_acquire()
rthread.gc_thread_run()
_after_thread_switch()
acquire._gctransformer_hint_cannot_collect_ = True
acquire._dont_reach_me_in_del_ = True
# The _gctransformer_hint_cannot_collect_ hack is needed for
# translations in which the *_external_call() functions are not inlined.
# They tell the gctransformer not to save and restore the local GC
# pointers in the shadow stack. This is necessary because the GIL is
# not held after the call to gil.release() or before the call
# to gil.acquire().
def yield_thread():
# explicitly release the gil, in a way that tries to give more
# priority to other threads (as opposed to continuing to run in
# the same thread).
if _gil_yield_thread():
from rpython.rlib import rthread
rthread.gc_thread_run()
_after_thread_switch()
yield_thread._gctransformer_hint_close_stack_ = True
yield_thread._dont_reach_me_in_del_ = True
yield_thread._dont_inline_ = True
# yield_thread() needs a different hint: _gctransformer_hint_close_stack_.
# The *_external_call() functions are themselves called only from the rffi
# module from a helper function that also has this hint.
|