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

Reply via email to