-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrgil.py
More file actions
146 lines (116 loc) · 5.42 KB
/
rgil.py
File metadata and controls
146 lines (116 loc) · 5.42 KB
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
146
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
from rpython.rlib.objectmodel import not_rpython
# 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
@not_rpython
def _after_thread_switch():
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.