Author: Armin Rigo <[email protected]>
Branch: cpyext-ext
Changeset: r83255:29df5133bc06
Date: 2016-03-22 17:04 +0100
http://bitbucket.org/pypy/pypy/changeset/29df5133bc06/
Log: Fix for test_frame_tstate_tracing:
* revert ce2053a9cdeb
* extend RPyThreadStart to RPyThreadStartEx with an extra 'arg'
* have PyThread_start_new_thread() call directly RPyThreadStartEx in
C
* fix a test problem where CPython kills the content of
'thread._local' instances as soon as a C-to-Python call returns, if
we're in a context where the C thread was not started with CPython's
official thread module
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -490,6 +490,7 @@
'PyThread_create_key', 'PyThread_delete_key', 'PyThread_set_key_value',
'PyThread_get_key_value', 'PyThread_delete_key_value',
'PyThread_ReInitTLS', 'PyThread_init_thread',
+ 'PyThread_start_new_thread',
'PyStructSequence_InitType', 'PyStructSequence_New',
'PyStructSequence_UnnamedField',
diff --git a/pypy/module/cpyext/include/pythread.h
b/pypy/module/cpyext/include/pythread.h
--- a/pypy/module/cpyext/include/pythread.h
+++ b/pypy/module/cpyext/include/pythread.h
@@ -18,6 +18,8 @@
#define NOWAIT_LOCK 0
PyAPI_FUNC(void) PyThread_release_lock(PyThread_type_lock);
+PyAPI_FUNC(long) PyThread_start_new_thread(void (*func)(void *), void *arg);
+
/* Thread Local Storage (TLS) API */
PyAPI_FUNC(int) PyThread_create_key(void);
PyAPI_FUNC(void) PyThread_delete_key(int);
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
@@ -3,6 +3,7 @@
from pypy.module.cpyext.pyobject import PyObject, Py_DecRef, make_ref, from_ref
from rpython.rtyper.lltypesystem import rffi, lltype
from rpython.rlib import rthread
+from rpython.rlib.objectmodel import we_are_translated
PyInterpreterStateStruct = lltype.ForwardReference()
PyInterpreterState = lltype.Ptr(PyInterpreterStateStruct)
@@ -54,15 +55,6 @@
return 0
return 1
-thread_func = lltype.Ptr(lltype.FuncType([rffi.VOIDP], lltype.Void))
-@cpython_api([thread_func, rffi.VOIDP], rffi.INT_real, error=-1, gil='release')
-def PyThread_start_new_thread(space, func, arg):
- from pypy.module.thread import os_thread
- w_args = space.newtuple([space.wrap(rffi.cast(lltype.Signed, arg)),])
- w_func = os_thread.W_WrapThreadFunc(func)
- os_thread.start_new_thread(space, w_func, w_args)
- return 0
-
# XXX: might be generally useful
def encapsulator(T, flavor='raw', dealloc=None):
class MemoryCapsule(object):
@@ -209,6 +201,23 @@
ExecutionContext.cpyext_gilstate_counter_noleave = 0
+def _workaround_cpython_untranslated(space):
+ # Workaround when not translated. The problem is that
+ # space.threadlocals.get_ec() is based on "thread._local", but
+ # CPython will clear a "thread._local" as soon as CPython's
+ # PyThreadState goes away. This occurs even if we're in a thread
+ # created from C and we're going to call some more Python code
+ # from this thread. This case shows up in
+ # test_pystate.test_frame_tstate_tracing.
+ def get_possibly_deleted_ec():
+ ec1 = space.threadlocals.raw_thread_local.get()
+ ec2 = space.threadlocals._valuedict.get(rthread.get_ident(), None)
+ if ec1 is None and ec2 is not None:
+ space.threadlocals.raw_thread_local.set(ec2)
+ return space.threadlocals.__class__.get_ec(space.threadlocals)
+ space.threadlocals.get_ec = get_possibly_deleted_ec
+
+
@cpython_api([], PyGILState_STATE, error=CANNOT_FAIL, gil="pygilstate_ensure")
def PyGILState_Ensure(space, previous_state):
# The argument 'previous_state' is not part of the API; it is inserted
@@ -228,6 +237,8 @@
# PyPy code below.
assert previous_state == PyGILState_UNLOCKED
assert ec.cpyext_gilstate_counter_noleave == 0
+ if not we_are_translated():
+ _workaround_cpython_untranslated(space)
#
return rffi.cast(PyGILState_STATE, previous_state)
diff --git a/pypy/module/cpyext/src/pythread.c
b/pypy/module/cpyext/src/pythread.c
--- a/pypy/module/cpyext/src/pythread.c
+++ b/pypy/module/cpyext/src/pythread.c
@@ -64,6 +64,13 @@
RPyThreadReleaseLock((struct RPyOpaque_ThreadLock*)lock);
}
+long
+PyThread_start_new_thread(void (*func)(void *), void *arg)
+{
+ PyThread_init_thread();
+ return RPyThreadStartEx(func, arg);
+}
+
/* ------------------------------------------------------------------------
Per-thread data ("key") support.
diff --git a/pypy/module/thread/os_thread.py b/pypy/module/thread/os_thread.py
--- a/pypy/module/thread/os_thread.py
+++ b/pypy/module/thread/os_thread.py
@@ -6,9 +6,7 @@
from rpython.rlib import rthread
from pypy.module.thread.error import wrap_thread_error
from pypy.interpreter.error import OperationError, oefmt
-from pypy.interpreter.gateway import unwrap_spec, Arguments, interp2app
-from pypy.interpreter.baseobjspace import W_Root
-from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import unwrap_spec, Arguments
# Here are the steps performed to start a new thread:
#
@@ -163,25 +161,6 @@
if w_threading is not None:
space.call_method(w_threading, "_after_fork")
-class W_WrapThreadFunc(W_Root):
- ''' Wrap a cpyext.pystate.thread_func, which
- has the signature void func(void *)
- '''
- def __init__(self, func):
- self.func = func
-
- def descr_call(self, space, w_arg):
- from rpython.rtyper.lltypesystem import rffi
- try:
- arg = rffi.cast(rffi.VOIDP, space.int_w(w_arg))
- self.func(arg)
- except Exception as e:
- import pdb;pdb.set_trace()
-
-W_WrapThreadFunc.typedef = TypeDef("hiddenclass",
- __call__ = interp2app(W_WrapThreadFunc.descr_call),
-)
-
def start_new_thread(space, w_callable, w_args, w_kwargs=None):
"""Start a new thread and return its identifier. The thread will call the
function with positional arguments from the tuple args and keyword arguments
diff --git a/rpython/translator/c/src/thread_nt.c
b/rpython/translator/c/src/thread_nt.c
--- a/rpython/translator/c/src/thread_nt.c
+++ b/rpython/translator/c/src/thread_nt.c
@@ -18,7 +18,8 @@
typedef struct RPyOpaque_ThreadLock NRMUTEX, *PNRMUTEX;
typedef struct {
- void (*func)(void);
+ void (*func)(void *);
+ void *arg;
long id;
HANDLE done;
} callobj;
@@ -30,20 +31,29 @@
{
callobj *obj = (callobj*)call;
/* copy callobj since other thread might free it before we're done */
- void (*func)(void) = obj->func;
+ void (*func)(void *) = obj->func;
+ void *arg = obj->arg;
obj->id = GetCurrentThreadId();
ReleaseSemaphore(obj->done, 1, NULL);
- func();
+ func(arg);
}
long RPyThreadStart(void (*func)(void))
{
+ /* a kind-of-invalid cast, but the 'func' passed here doesn't expect
+ any argument, so it's unlikely to cause problems */
+ return RPyThreadStartEx((void(*)(void *))func, NULL);
+}
+
+long RPyThreadStartEx(void (*func)(void *), void *arg)
+{
unsigned long rv;
callobj obj;
obj.id = -1; /* guilty until proved innocent */
obj.func = func;
+ obj.arg = arg;
obj.done = CreateSemaphore(NULL, 0, 1, NULL);
if (obj.done == NULL)
return -1;
diff --git a/rpython/translator/c/src/thread_nt.h
b/rpython/translator/c/src/thread_nt.h
--- a/rpython/translator/c/src/thread_nt.h
+++ b/rpython/translator/c/src/thread_nt.h
@@ -15,6 +15,8 @@
RPY_EXTERN
long RPyThreadStart(void (*func)(void));
RPY_EXTERN
+long RPyThreadStartEx(void (*func)(void *), void *arg);
+RPY_EXTERN
int RPyThreadLockInit(struct RPyOpaque_ThreadLock *lock);
RPY_EXTERN
void RPyOpaqueDealloc_ThreadLock(struct RPyOpaque_ThreadLock *lock);
diff --git a/rpython/translator/c/src/thread_pthread.c
b/rpython/translator/c/src/thread_pthread.c
--- a/rpython/translator/c/src/thread_pthread.c
+++ b/rpython/translator/c/src/thread_pthread.c
@@ -58,13 +58,14 @@
static long _pypythread_stacksize = 0;
-static void *bootstrap_pthread(void *func)
+long RPyThreadStart(void (*func)(void))
{
- ((void(*)(void))func)();
- return NULL;
+ /* a kind-of-invalid cast, but the 'func' passed here doesn't expect
+ any argument, so it's unlikely to cause problems */
+ return RPyThreadStartEx((void(*)(void *))func, NULL);
}
-long RPyThreadStart(void (*func)(void))
+long RPyThreadStartEx(void (*func)(void *), void *arg)
{
pthread_t th;
int status;
@@ -94,8 +95,12 @@
#else
(pthread_attr_t*)NULL,
#endif
- bootstrap_pthread,
- (void *)func
+ /* the next line does an invalid cast: pthread_create() will see a
+ function that returns random garbage. The code is the same as
+ CPython: this random garbage will be stored for pthread_join()
+ to return, but in this case pthread_join() is never called. */
+ (void* (*)(void *))func,
+ (void *)arg
);
#if defined(THREAD_STACK_SIZE) || defined(PTHREAD_SYSTEM_SCHED_SUPPORTED)
diff --git a/rpython/translator/c/src/thread_pthread.h
b/rpython/translator/c/src/thread_pthread.h
--- a/rpython/translator/c/src/thread_pthread.h
+++ b/rpython/translator/c/src/thread_pthread.h
@@ -62,6 +62,8 @@
RPY_EXTERN
long RPyThreadStart(void (*func)(void));
RPY_EXTERN
+long RPyThreadStartEx(void (*func)(void *), void *arg);
+RPY_EXTERN
int RPyThreadLockInit(struct RPyOpaque_ThreadLock *lock);
RPY_EXTERN
void RPyOpaqueDealloc_ThreadLock(struct RPyOpaque_ThreadLock *lock);
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit