PageRenderTime 75ms CodeModel.GetById 30ms app.highlight 24ms RepoModel.GetById 16ms app.codeStats 0ms

/pypy/module/cpyext/pystate.py

https://bitbucket.org/pypy/pypy/
Python | 315 lines | 220 code | 24 blank | 71 comment | 13 complexity | c076cf5ee780880a382edb8eddd04a10 MD5 | raw file
  1from pypy.module.cpyext.api import (
  2    cpython_api, generic_cpy_call, CANNOT_FAIL, CConfig, cpython_struct)
  3from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref, from_ref
  4from rpython.rtyper.lltypesystem import rffi, lltype
  5from rpython.rlib import rthread
  6from rpython.rlib.objectmodel import we_are_translated
  7
  8PyInterpreterStateStruct = lltype.ForwardReference()
  9PyInterpreterState = lltype.Ptr(PyInterpreterStateStruct)
 10cpython_struct(
 11    "PyInterpreterState",
 12    [('next', PyInterpreterState)],
 13    PyInterpreterStateStruct)
 14PyThreadState = lltype.Ptr(cpython_struct(
 15    "PyThreadState",
 16    [('interp', PyInterpreterState),
 17     ('dict', PyObject),
 18     ]))
 19
 20class NoThreads(Exception):
 21    pass
 22
 23@cpython_api([], PyThreadState, error=CANNOT_FAIL, gil="release")
 24def PyEval_SaveThread(space):
 25    """Release the global interpreter lock (if it has been created and thread
 26    support is enabled) and reset the thread state to NULL, returning the
 27    previous thread state.  If the lock has been created,
 28    the current thread must have acquired it.  (This function is available even
 29    when thread support is disabled at compile time.)"""
 30    state = space.fromcache(InterpreterState)
 31    tstate = state.swap_thread_state(
 32        space, lltype.nullptr(PyThreadState.TO))
 33    return tstate
 34
 35@cpython_api([PyThreadState], lltype.Void, gil="acquire")
 36def PyEval_RestoreThread(space, tstate):
 37    """Acquire the global interpreter lock (if it has been created and thread
 38    support is enabled) and set the thread state to tstate, which must not be
 39    NULL.  If the lock has been created, the current thread must not have
 40    acquired it, otherwise deadlock ensues.  (This function is available even
 41    when thread support is disabled at compile time.)"""
 42    state = space.fromcache(InterpreterState)
 43    state.swap_thread_state(space, tstate)
 44
 45@cpython_api([], lltype.Void)
 46def PyEval_InitThreads(space):
 47    if not space.config.translation.thread:
 48        raise NoThreads
 49    from pypy.module.thread import os_thread
 50    os_thread.setup_threads(space)
 51
 52@cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
 53def PyEval_ThreadsInitialized(space):
 54    if not space.config.translation.thread:
 55        return 0
 56    from pypy.module.thread import os_thread
 57    return int(os_thread.threads_initialized(space))
 58
 59# XXX: might be generally useful
 60def encapsulator(T, flavor='raw', dealloc=None):
 61    class MemoryCapsule(object):
 62        def __init__(self, space):
 63            self.space = space
 64            if space is not None:
 65                self.memory = lltype.malloc(T, flavor=flavor)
 66            else:
 67                self.memory = lltype.nullptr(T)
 68        def __del__(self):
 69            if self.memory:
 70                if dealloc and self.space:
 71                    dealloc(self.memory, self.space)
 72                lltype.free(self.memory, flavor=flavor)
 73    return MemoryCapsule
 74
 75def ThreadState_dealloc(ts, space):
 76    assert space is not None
 77    Py_DecRef(space, ts.c_dict)
 78ThreadStateCapsule = encapsulator(PyThreadState.TO,
 79                                  dealloc=ThreadState_dealloc)
 80
 81from pypy.interpreter.executioncontext import ExecutionContext
 82
 83# Keep track of the ThreadStateCapsule for a particular execution context.  The
 84# default is for new execution contexts not to have one; it is allocated on the
 85# first cpyext-based request for it.
 86ExecutionContext.cpyext_threadstate = ThreadStateCapsule(None)
 87
 88# Also keep track of whether it has been initialized yet or not (None is a valid
 89# PyThreadState for an execution context to have, when the GIL has been
 90# released, so a check against that can't be used to determine the need for
 91# initialization).
 92ExecutionContext.cpyext_initialized_threadstate = False
 93
 94def cleanup_cpyext_state(self):
 95    self.cpyext_threadstate = None
 96    self.cpyext_initialized_threadstate = False
 97ExecutionContext.cleanup_cpyext_state = cleanup_cpyext_state
 98
 99class InterpreterState(object):
100    def __init__(self, space):
101        self.interpreter_state = lltype.malloc(
102            PyInterpreterState.TO, flavor='raw', zero=True, immortal=True)
103
104    def new_thread_state(self, space):
105        """
106        Create a new ThreadStateCapsule to hold the PyThreadState for a
107        particular execution context.
108
109        :param space: A space.
110
111        :returns: A new ThreadStateCapsule holding a newly allocated
112            PyThreadState and referring to this interpreter state.
113        """
114        capsule = ThreadStateCapsule(space)
115        ts = capsule.memory
116        ts.c_interp = self.interpreter_state
117        ts.c_dict = make_ref(space, space.newdict())
118        return capsule
119
120
121    def get_thread_state(self, space):
122        """
123        Get the current PyThreadState for the current execution context.
124
125        :param space: A space.
126
127        :returns: The current PyThreadState for the current execution context,
128            or None if it does not have one.
129        """
130        ec = space.getexecutioncontext()
131        return self._get_thread_state(space, ec).memory
132
133
134    def swap_thread_state(self, space, tstate):
135        """
136        Replace the current thread state of the current execution context with a
137        new thread state.
138
139        :param space: The space.
140
141        :param tstate: The new PyThreadState for the current execution context.
142
143        :returns: The old thread state for the current execution context, either
144            None or a PyThreadState.
145        """
146        ec = space.getexecutioncontext()
147        capsule = self._get_thread_state(space, ec)
148        old_tstate = capsule.memory
149        capsule.memory = tstate
150        return old_tstate
151
152    def _get_thread_state(self, space, ec):
153        """
154        Get the ThreadStateCapsule for the given execution context, possibly
155        creating a new one if it does not already have one.
156
157        :param space: The space.
158        :param ec: The ExecutionContext of which to get the thread state.
159        :returns: The ThreadStateCapsule for the given execution context.
160        """
161        if not ec.cpyext_initialized_threadstate:
162            ec.cpyext_threadstate = self.new_thread_state(space)
163            ec.cpyext_initialized_threadstate = True
164        return ec.cpyext_threadstate
165
166@cpython_api([], PyThreadState, error=CANNOT_FAIL)
167def PyThreadState_Get(space):
168    state = space.fromcache(InterpreterState)
169    return state.get_thread_state(space)
170
171@cpython_api([], PyObject, result_is_ll=True, error=CANNOT_FAIL)
172def PyThreadState_GetDict(space):
173    """Return a dictionary in which extensions can store thread-specific state
174    information.  Each extension should use a unique key to use to store state in
175    the dictionary.  It is okay to call this function when no current thread state
176    is available. If this function returns NULL, no exception has been raised and
177    the caller should assume no current thread state is available.
178
179    Previously this could only be called when a current thread is active, and NULL
180    meant that an exception was raised."""
181    state = space.fromcache(InterpreterState)
182    return state.get_thread_state(space).c_dict
183
184@cpython_api([PyThreadState], PyThreadState, error=CANNOT_FAIL)
185def PyThreadState_Swap(space, tstate):
186    """Swap the current thread state with the thread state given by the argument
187    tstate, which may be NULL.  The global interpreter lock must be held."""
188    state = space.fromcache(InterpreterState)
189    return state.swap_thread_state(space, tstate)
190
191@cpython_api([PyThreadState], lltype.Void, gil="acquire")
192def PyEval_AcquireThread(space, tstate):
193    """Acquire the global interpreter lock and set the current thread state to
194    tstate, which should not be NULL.  The lock must have been created earlier.
195    If this thread already has the lock, deadlock ensues.  This function is not
196    available when thread support is disabled at compile time."""
197
198@cpython_api([PyThreadState], lltype.Void, gil="release")
199def PyEval_ReleaseThread(space, tstate):
200    """Reset the current thread state to NULL and release the global interpreter
201    lock.  The lock must have been created earlier and must be held by the current
202    thread.  The tstate argument, which must not be NULL, is only used to check
203    that it represents the current thread state --- if it isn't, a fatal error is
204    reported. This function is not available when thread support is disabled at
205    compile time."""
206
207PyGILState_STATE = rffi.INT
208PyGILState_LOCKED = 0
209PyGILState_UNLOCKED = 1
210PyGILState_IGNORE = 2
211
212ExecutionContext.cpyext_gilstate_counter_noleave = 0
213
214def _workaround_cpython_untranslated(space):
215    # Workaround when not translated.  The problem is that
216    # space.threadlocals.get_ec() is based on "thread._local", but
217    # CPython will clear a "thread._local" as soon as CPython's
218    # PyThreadState goes away.  This occurs even if we're in a thread
219    # created from C and we're going to call some more Python code
220    # from this thread.  This case shows up in
221    # test_pystate.test_frame_tstate_tracing.
222    def get_possibly_deleted_ec():
223        ec1 = space.threadlocals.raw_thread_local.get()
224        ec2 = space.threadlocals._valuedict.get(rthread.get_ident(), None)
225        if ec1 is None and ec2 is not None:
226            space.threadlocals.raw_thread_local.set(ec2)
227        return space.threadlocals.__class__.get_ec(space.threadlocals)
228    space.threadlocals.get_ec = get_possibly_deleted_ec
229
230
231@cpython_api([], PyGILState_STATE, error=CANNOT_FAIL, gil="pygilstate_ensure")
232def PyGILState_Ensure(space, previous_state):
233    # The argument 'previous_state' is not part of the API; it is inserted
234    # by make_wrapper() and contains PyGILState_LOCKED/UNLOCKED based on
235    # the previous GIL state.
236    must_leave = space.threadlocals.try_enter_thread(space)
237    ec = space.getexecutioncontext()
238    if not must_leave:
239        # This is a counter of how many times we called try_enter_thread()
240        # and it returned False.  In PyGILState_Release(), if this counter
241        # is greater than zero, we decrement it; only if the counter is
242        # already zero do we call leave_thread().
243        ec.cpyext_gilstate_counter_noleave += 1
244    else:
245        # This case is for when we just built a fresh threadlocals.
246        # We should only see it when we are in a new thread with no
247        # PyPy code below.
248        assert previous_state == PyGILState_UNLOCKED
249        assert ec.cpyext_gilstate_counter_noleave == 0
250        if not we_are_translated():
251            _workaround_cpython_untranslated(space)
252    #
253    return rffi.cast(PyGILState_STATE, previous_state)
254
255@cpython_api([PyGILState_STATE], lltype.Void, gil="pygilstate_release")
256def PyGILState_Release(space, oldstate):
257    oldstate = rffi.cast(lltype.Signed, oldstate)
258    ec = space.getexecutioncontext()
259    if ec.cpyext_gilstate_counter_noleave > 0:
260        ec.cpyext_gilstate_counter_noleave -= 1
261    else:
262        assert ec.cpyext_gilstate_counter_noleave == 0
263        assert oldstate == PyGILState_UNLOCKED
264        space.threadlocals.leave_thread(space)
265
266@cpython_api([], PyInterpreterState, error=CANNOT_FAIL)
267def PyInterpreterState_Head(space):
268    """Return the interpreter state object at the head of the list of all such objects.
269    """
270    return space.fromcache(InterpreterState).interpreter_state
271
272@cpython_api([PyInterpreterState], PyInterpreterState, error=CANNOT_FAIL)
273def PyInterpreterState_Next(space, interp):
274    """Return the next interpreter state object after interp from the list of all
275    such objects.
276    """
277    return lltype.nullptr(PyInterpreterState.TO)
278
279@cpython_api([PyInterpreterState], PyThreadState, error=CANNOT_FAIL,
280             gil="around")
281def PyThreadState_New(space, interp):
282    """Create a new thread state object belonging to the given interpreter
283    object.  The global interpreter lock need not be held, but may be held if
284    it is necessary to serialize calls to this function."""
285    if not space.config.translation.thread:
286        raise NoThreads
287    # PyThreadState_Get will allocate a new execution context,
288    # we need to protect gc and other globals with the GIL.
289    rthread.gc_thread_start()
290    return PyThreadState_Get(space)
291
292@cpython_api([PyThreadState], lltype.Void)
293def PyThreadState_Clear(space, tstate):
294    """Reset all information in a thread state object.  The global
295    interpreter lock must be held."""
296    if not space.config.translation.thread:
297        raise NoThreads
298    Py_DecRef(space, tstate.c_dict)
299    tstate.c_dict = lltype.nullptr(PyObject.TO)
300    space.threadlocals.leave_thread(space)
301    space.getexecutioncontext().cleanup_cpyext_state()
302    rthread.gc_thread_die()
303
304@cpython_api([PyThreadState], lltype.Void)
305def PyThreadState_Delete(space, tstate):
306    """Destroy a thread state object.  The global interpreter lock need not
307    be held.  The thread state must have been reset with a previous call to
308    PyThreadState_Clear()."""
309
310@cpython_api([], lltype.Void)
311def PyThreadState_DeleteCurrent(space):
312    """Destroy a thread state object.  The global interpreter lock need not
313    be held.  The thread state must have been reset with a previous call to
314    PyThreadState_Clear()."""
315