/pypy/module/cpyext/test/test_pystate.py

https://bitbucket.org/pypy/pypy/ · Python · 286 lines · 166 code · 19 blank · 101 comment · 6 complexity · 887b4519d59fc83e5181a4b3346afec6 MD5 · raw file

  1. import py, pytest
  2. from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
  3. from pypy.module.cpyext.test.test_api import BaseApiTest
  4. from rpython.rtyper.lltypesystem.lltype import nullptr
  5. from pypy.module.cpyext.pystate import PyInterpreterState, PyThreadState
  6. from pypy.module.cpyext.pyobject import from_ref
  7. from rpython.rtyper.lltypesystem import lltype
  8. from pypy.module.cpyext.test.test_cpyext import LeakCheckingTest, freeze_refcnts
  9. from pypy.module.cpyext.pystate import PyThreadState_Get, PyInterpreterState_Head
  10. from rpython.tool import leakfinder
  11. class AppTestThreads(AppTestCpythonExtensionBase):
  12. def test_allow_threads(self):
  13. module = self.import_extension('foo', [
  14. ("test", "METH_NOARGS",
  15. """
  16. Py_BEGIN_ALLOW_THREADS
  17. {
  18. Py_BLOCK_THREADS
  19. Py_UNBLOCK_THREADS
  20. }
  21. Py_END_ALLOW_THREADS
  22. Py_RETURN_NONE;
  23. """),
  24. ])
  25. # Should compile at least
  26. module.test()
  27. def test_gilstate(self):
  28. module = self.import_extension('foo', [
  29. ("double_ensure", "METH_O",
  30. '''
  31. PyGILState_STATE state0, state1;
  32. int val = PyLong_AsLong(args);
  33. PyEval_InitThreads();
  34. state0 = PyGILState_Ensure(); /* hangs here */
  35. if (val != 0)
  36. {
  37. state1 = PyGILState_Ensure();
  38. PyGILState_Release(state1);
  39. }
  40. PyGILState_Release(state0);
  41. Py_RETURN_NONE;
  42. '''),
  43. ])
  44. module.double_ensure(0)
  45. print '0 ok'
  46. module.double_ensure(1)
  47. print '1 ok'
  48. def test_thread_state_get(self):
  49. module = self.import_extension('foo', [
  50. ("get", "METH_NOARGS",
  51. """
  52. PyThreadState *tstate = PyThreadState_Get();
  53. if (tstate == NULL) {
  54. return PyLong_FromLong(0);
  55. }
  56. if (tstate->interp != PyInterpreterState_Head()) {
  57. return PyLong_FromLong(1);
  58. }
  59. if (tstate->interp->next != NULL) {
  60. return PyLong_FromLong(2);
  61. }
  62. return PyLong_FromLong(3);
  63. """),
  64. ])
  65. assert module.get() == 3
  66. def test_basic_threadstate_dance(self):
  67. if self.runappdirect:
  68. py.test.xfail('segfault: on cpython cannot Get() a NULL tstate')
  69. module = self.import_extension('foo', [
  70. ("dance", "METH_NOARGS",
  71. """
  72. PyThreadState *old_tstate, *new_tstate;
  73. PyEval_InitThreads();
  74. old_tstate = PyThreadState_Swap(NULL);
  75. if (old_tstate == NULL) {
  76. return PyLong_FromLong(0);
  77. }
  78. new_tstate = PyThreadState_Get(); /* fails on cpython */
  79. if (new_tstate != NULL) {
  80. return PyLong_FromLong(1);
  81. }
  82. new_tstate = PyThreadState_Swap(old_tstate);
  83. if (new_tstate != NULL) {
  84. return PyLong_FromLong(2);
  85. }
  86. new_tstate = PyThreadState_Get();
  87. if (new_tstate != old_tstate) {
  88. return PyLong_FromLong(3);
  89. }
  90. return PyLong_FromLong(4);
  91. """),
  92. ])
  93. assert module.dance() == 4
  94. def test_threadstate_dict(self):
  95. module = self.import_extension('foo', [
  96. ("getdict", "METH_NOARGS",
  97. """
  98. PyObject *dict = PyThreadState_GetDict();
  99. Py_INCREF(dict);
  100. return dict;
  101. """),
  102. ])
  103. assert isinstance(module.getdict(), dict)
  104. def test_savethread(self):
  105. module = self.import_extension('foo', [
  106. ("bounce", "METH_NOARGS",
  107. """
  108. PyThreadState * tstate;
  109. if (PyEval_ThreadsInitialized() == 0)
  110. {
  111. PyEval_InitThreads();
  112. }
  113. PyGILState_Ensure();
  114. tstate = PyEval_SaveThread();
  115. if (tstate == NULL) {
  116. return PyLong_FromLong(0);
  117. }
  118. PyEval_RestoreThread(tstate);
  119. if (PyThreadState_Get() != tstate) {
  120. return PyLong_FromLong(1);
  121. }
  122. return PyLong_FromLong(3);
  123. """),
  124. ])
  125. res = module.bounce()
  126. assert res == 3
  127. def test_threadsinitialized(self):
  128. module = self.import_extension('foo', [
  129. ("test", "METH_NOARGS",
  130. """
  131. return PyInt_FromLong(PyEval_ThreadsInitialized());
  132. """),
  133. ])
  134. res = module.test()
  135. print "got", res
  136. assert res in (0, 1)
  137. class AppTestState(AppTestCpythonExtensionBase):
  138. def test_frame_tstate_tracing(self):
  139. import sys, threading
  140. module = self.import_extension('foo', [
  141. ("call_in_temporary_c_thread", "METH_O",
  142. """
  143. PyObject *res = NULL;
  144. test_c_thread_t test_c_thread;
  145. long thread;
  146. PyEval_InitThreads();
  147. test_c_thread.start_event = PyThread_allocate_lock();
  148. test_c_thread.exit_event = PyThread_allocate_lock();
  149. test_c_thread.callback = NULL;
  150. if (!test_c_thread.start_event || !test_c_thread.exit_event) {
  151. PyErr_SetString(PyExc_RuntimeError, "could not allocate lock");
  152. goto exit;
  153. }
  154. Py_INCREF(args);
  155. test_c_thread.callback = args;
  156. PyThread_acquire_lock(test_c_thread.start_event, 1);
  157. PyThread_acquire_lock(test_c_thread.exit_event, 1);
  158. thread = PyThread_start_new_thread(temporary_c_thread, &test_c_thread);
  159. if (thread == -1) {
  160. PyErr_SetString(PyExc_RuntimeError, "unable to start the thread");
  161. PyThread_release_lock(test_c_thread.start_event);
  162. PyThread_release_lock(test_c_thread.exit_event);
  163. goto exit;
  164. }
  165. PyThread_acquire_lock(test_c_thread.start_event, 1);
  166. PyThread_release_lock(test_c_thread.start_event);
  167. Py_BEGIN_ALLOW_THREADS
  168. PyThread_acquire_lock(test_c_thread.exit_event, 1);
  169. PyThread_release_lock(test_c_thread.exit_event);
  170. Py_END_ALLOW_THREADS
  171. Py_INCREF(Py_None);
  172. res = Py_None;
  173. exit:
  174. Py_CLEAR(test_c_thread.callback);
  175. if (test_c_thread.start_event)
  176. PyThread_free_lock(test_c_thread.start_event);
  177. if (test_c_thread.exit_event)
  178. PyThread_free_lock(test_c_thread.exit_event);
  179. return res;
  180. """), ], prologue = """
  181. #include "pythread.h"
  182. typedef struct {
  183. PyThread_type_lock start_event;
  184. PyThread_type_lock exit_event;
  185. PyObject *callback;
  186. } test_c_thread_t;
  187. static void
  188. temporary_c_thread(void *data)
  189. {
  190. test_c_thread_t *test_c_thread = data;
  191. PyGILState_STATE state;
  192. PyObject *res;
  193. PyThread_release_lock(test_c_thread->start_event);
  194. /* Allocate a Python thread state for this thread */
  195. state = PyGILState_Ensure();
  196. res = PyObject_CallFunction(test_c_thread->callback, "", NULL);
  197. Py_CLEAR(test_c_thread->callback);
  198. if (res == NULL) {
  199. PyErr_Print();
  200. }
  201. else {
  202. Py_DECREF(res);
  203. }
  204. /* Destroy the Python thread state for this thread */
  205. PyGILState_Release(state);
  206. PyThread_release_lock(test_c_thread->exit_event);
  207. /*PyThread_exit_thread(); NOP (on linux) and not implememnted */
  208. };
  209. """)
  210. def noop_trace(frame, event, arg):
  211. # no operation
  212. return noop_trace
  213. def generator():
  214. while 1:
  215. yield "genereator"
  216. def callback():
  217. if callback.gen is None:
  218. callback.gen = generator()
  219. return next(callback.gen)
  220. callback.gen = None
  221. old_trace = sys.gettrace()
  222. sys.settrace(noop_trace)
  223. try:
  224. # Install a trace function
  225. threading.settrace(noop_trace)
  226. # Create a generator in a C thread which exits after the call
  227. module.call_in_temporary_c_thread(callback)
  228. # Call the generator in a different Python thread, check that the
  229. # generator didn't keep a reference to the destroyed thread state
  230. for test in range(3):
  231. # The trace function is still called here
  232. callback()
  233. finally:
  234. sys.settrace(old_trace)
  235. class TestInterpreterState(BaseApiTest):
  236. def test_interpreter_head(self, space, api):
  237. state = api.PyInterpreterState_Head()
  238. assert state != nullptr(PyInterpreterState.TO)
  239. def test_interpreter_next(self, space, api):
  240. state = api.PyInterpreterState_Head()
  241. assert nullptr(PyInterpreterState.TO) == api.PyInterpreterState_Next(state)