PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/cpyext/pystate.py

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