PageRenderTime 37ms CodeModel.GetById 28ms app.highlight 6ms RepoModel.GetById 1ms app.codeStats 0ms

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

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