Author: Amaury Forgeot d'Arc <[email protected]>
Branch:
Changeset: r58333:6822a5e1abe8
Date: 2012-10-21 15:15 +0200
http://bitbucket.org/pypy/pypy/changeset/6822a5e1abe8/
Log: Merge branch cpyext-PyThreadState_New: implement threadstate-related
functions.
Should be able to start a thread in C, and have it call back into
Python.
diff --git a/pypy/module/cpyext/pystate.py b/pypy/module/cpyext/pystate.py
--- a/pypy/module/cpyext/pystate.py
+++ b/pypy/module/cpyext/pystate.py
@@ -1,7 +1,8 @@
from pypy.module.cpyext.api import (
cpython_api, generic_cpy_call, CANNOT_FAIL, CConfig, cpython_struct)
-from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref
+from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref, from_ref
from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.module.thread import ll_thread, os_thread
PyInterpreterStateStruct = lltype.ForwardReference()
PyInterpreterState = lltype.Ptr(PyInterpreterStateStruct)
@@ -43,7 +44,7 @@
@cpython_api([], lltype.Void)
def PyEval_InitThreads(space):
- return
+ os_thread.setup_threads(space)
@cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
def PyEval_ThreadsInitialized(space):
@@ -85,10 +86,7 @@
ExecutionContext.cpyext_initialized_threadstate = False
def cleanup_cpyext_state(self):
- try:
- del self.cpyext_threadstate
- except AttributeError:
- pass
+ self.cpyext_threadstate = None
self.cpyext_initialized_threadstate = False
ExecutionContext.cleanup_cpyext_state = cleanup_cpyext_state
@@ -182,8 +180,9 @@
tstate, which should not be NULL. The lock must have been created earlier.
If this thread already has the lock, deadlock ensues. This function is not
available when thread support is disabled at compile time."""
- # All cpyext calls release and acquire the GIL, so this is not necessary.
- pass
+ if rffi.aroundstate.after:
+ # After external call is before entering Python
+ rffi.aroundstate.after()
@cpython_api([PyThreadState], lltype.Void)
def PyEval_ReleaseThread(space, tstate):
@@ -193,8 +192,9 @@
that it represents the current thread state --- if it isn't, a fatal error
is
reported. This function is not available when thread support is disabled at
compile time."""
- # All cpyext calls release and acquire the GIL, so this is not necessary.
- pass
+ if rffi.aroundstate.before:
+ # Before external call is after running Python
+ rffi.aroundstate.before()
PyGILState_STATE = rffi.COpaquePtr('PyGILState_STATE',
typedef='PyGILState_STATE',
@@ -202,13 +202,16 @@
@cpython_api([], PyGILState_STATE, error=CANNOT_FAIL)
def PyGILState_Ensure(space):
- # All cpyext calls release and acquire the GIL, so this is not necessary.
+ if rffi.aroundstate.after:
+ # After external call is before entering Python
+ rffi.aroundstate.after()
return 0
@cpython_api([PyGILState_STATE], lltype.Void)
def PyGILState_Release(space, state):
- # All cpyext calls release and acquire the GIL, so this is not necessary.
- return
+ if rffi.aroundstate.before:
+ # Before external call is after running Python
+ rffi.aroundstate.before()
@cpython_api([], PyInterpreterState, error=CANNOT_FAIL)
def PyInterpreterState_Head(space):
@@ -222,3 +225,41 @@
such objects.
"""
return lltype.nullptr(PyInterpreterState.TO)
+
+@cpython_api([PyInterpreterState], PyThreadState, error=CANNOT_FAIL)
+def PyThreadState_New(space, interp):
+ """Create a new thread state object belonging to the given interpreter
+ object. The global interpreter lock need not be held, but may be held if
+ it is necessary to serialize calls to this function."""
+ ll_thread.gc_thread_prepare()
+ # PyThreadState_Get will allocate a new execution context,
+ # we need to protect gc and other globals with the GIL.
+ rffi.aroundstate.after()
+ try:
+ ll_thread.gc_thread_start()
+ return PyThreadState_Get(space)
+ finally:
+ rffi.aroundstate.before()
+
+@cpython_api([PyThreadState], lltype.Void)
+def PyThreadState_Clear(space, tstate):
+ """Reset all information in a thread state object. The global
+ interpreter lock must be held."""
+ Py_DecRef(space, tstate.c_dict)
+ tstate.c_dict = lltype.nullptr(PyObject.TO)
+ space.threadlocals.leave_thread(space)
+ space.getexecutioncontext().cleanup_cpyext_state()
+ ll_thread.gc_thread_die()
+
+@cpython_api([PyThreadState], lltype.Void)
+def PyThreadState_Delete(space, tstate):
+ """Destroy a thread state object. The global interpreter lock need not
+ be held. The thread state must have been reset with a previous call to
+ PyThreadState_Clear()."""
+
+@cpython_api([], lltype.Void)
+def PyThreadState_DeleteCurrent(space):
+ """Destroy a thread state object. The global interpreter lock need not
+ be held. The thread state must have been reset with a previous call to
+ PyThreadState_Clear()."""
+
diff --git a/pypy/module/cpyext/stubsactive.py
b/pypy/module/cpyext/stubsactive.py
--- a/pypy/module/cpyext/stubsactive.py
+++ b/pypy/module/cpyext/stubsactive.py
@@ -14,26 +14,6 @@
PyFile_DecUseCount() functions described below as appropriate."""
raise NotImplementedError
-@cpython_api([PyInterpreterState], PyThreadState, error=CANNOT_FAIL)
-def PyThreadState_New(space, interp):
- """Create a new thread state object belonging to the given interpreter
object.
- The global interpreter lock need not be held, but may be held if it is
- necessary to serialize calls to this function."""
- raise NotImplementedError
-
-@cpython_api([PyThreadState], lltype.Void)
-def PyThreadState_Clear(space, tstate):
- """Reset all information in a thread state object. The global interpreter
lock
- must be held."""
- raise NotImplementedError
-
-@cpython_api([PyThreadState], lltype.Void)
-def PyThreadState_Delete(space, tstate):
- """Destroy a thread state object. The global interpreter lock need not be
held.
- The thread state must have been reset with a previous call to
- PyThreadState_Clear()."""
- raise NotImplementedError
-
@cpython_api([], rffi.INT_real, error=CANNOT_FAIL)
def Py_MakePendingCalls(space):
return 0
diff --git a/pypy/module/cpyext/test/callback_in_thread.c
b/pypy/module/cpyext/test/callback_in_thread.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/callback_in_thread.c
@@ -0,0 +1,83 @@
+/* A test module that spawns a thread and run a function there */
+
+#include <Python.h>
+#include <pthread.h>
+
+struct thread_data {
+ PyInterpreterState *interp;
+ PyObject *callback;
+};
+
+static void *thread_function(void* ptr) {
+ struct thread_data *data = (struct thread_data *)ptr;
+ PyInterpreterState *interp = data->interp;
+ /* Assuming you have access to an interpreter object, the typical
+ * idiom for calling into Python from a C thread is: */
+
+ PyThreadState *tstate;
+ PyObject *result;
+
+ /* interp is your reference to an interpreter object. */
+ tstate = PyThreadState_New(interp);
+ PyEval_AcquireThread(tstate);
+
+ /* Perform Python actions here. */
+ result = PyObject_CallFunction(data->callback,
+ "l", (long)pthread_self());
+ if (!result)
+ PyErr_Print();
+ else
+ Py_DECREF(result);
+
+ Py_DECREF(data->callback);
+
+ /* XXX Python examples don't mention it, but docs say that
+ * PyThreadState_Delete requires it. */
+ PyThreadState_Clear(tstate);
+
+ /* Release the thread. No Python API allowed beyond this point. */
+ PyEval_ReleaseThread(tstate);
+
+ /* You can either delete the thread state, or save it
+ until you need it the next time. */
+ PyThreadState_Delete(tstate);
+
+ free(data);
+ return NULL;
+}
+
+static PyObject *
+run_callback(PyObject *self, PyObject *callback)
+{
+ pthread_t thread;
+ struct thread_data *data = malloc(sizeof(struct thread_data));
+ Py_INCREF(callback);
+ data->interp = PyThreadState_Get()->interp;
+ data->callback = callback;
+ pthread_create(&thread, NULL, thread_function, (void*)data);
+ Py_RETURN_NONE;
+}
+
+
+static PyMethodDef module_functions[] = {
+ {"callInThread", (PyCFunction)run_callback, METH_O, NULL},
+ {NULL, NULL} /* Sentinel */
+};
+
+
+void initcallback_in_thread(void)
+{
+ PyObject *m;
+ m = Py_InitModule("callback_in_thread", module_functions);
+ if (m == NULL)
+ return;
+ PyEval_InitThreads();
+}
+
+/*
+cc -g -O0 -c callback_in_thread.c -I /usr/include/python2.6 -fPIC && ld -g -O0
callback_in_thread.o --shared -o callback_in_thread.so && gdb --args
~/python/cpython2.7/python -c "from __future__ import print_function; import
threading, time; from callback_in_thread import callInThread;
callInThread(print); time.sleep(1)"
+
+
+cc -g -O0 -c callback_in_thread.c -I ~/pypy/pypy/include -fPIC && ld -g -O0
callback_in_thread.o --shared -o callback_in_thread.pypy-19.so && gdb --args
~/pypy/pypy/pypy/pypy-c -c "from __future__ import print_function; import
threading, time; from callback_in_thread import callInThread;
callInThread(print); time.sleep(1)"
+
+ */
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit