Author: Armin Rigo <ar...@tunes.org>
Branch: ec-keepalive
Changeset: r81540:a4175679cd80
Date: 2016-01-04 11:32 +0100
http://bitbucket.org/pypy/pypy/changeset/a4175679cd80/

Log:    Finish the RPython part

diff --git a/rpython/rlib/rthread.py b/rpython/rlib/rthread.py
--- a/rpython/rlib/rthread.py
+++ b/rpython/rlib/rthread.py
@@ -352,6 +352,8 @@
     # A thread-local that points to an object.  The object stored in such
     # a thread-local is kept alive as long as the thread is not finished
     # (but only with our own GCs!  it seems not to work with Boehm...)
+    # (also, on Windows, if you're not making a DLL but an EXE, it will
+    # leak the objects when a thread finishes; see threadlocal.c.)
     _COUNT = 1
 
     def __init__(self, Cls, loop_invariant=False):
diff --git a/rpython/rlib/test/test_rthread.py 
b/rpython/rlib/test/test_rthread.py
--- a/rpython/rlib/test/test_rthread.py
+++ b/rpython/rlib/test/test_rthread.py
@@ -241,10 +241,13 @@
 class TestUsingFramework(AbstractThreadTests):
     gcpolicy = 'minimark'
 
-    def test_tlref_keepalive(self):
+    def test_tlref_keepalive(self, no__thread=True):
         import weakref
         from rpython.config.translationoption import SUPPORT__THREAD
 
+        if not (SUPPORT__THREAD or no__thread):
+            py.test.skip("no __thread support here")
+
         class FooBar(object):
             pass
         t = ThreadLocalReference(FooBar)
@@ -256,6 +259,10 @@
             return weakref.ref(x1)
         tset._dont_inline_ = True
 
+        class WrFromThread:
+            pass
+        wr_from_thread = WrFromThread()
+
         def f():
             assert t.automatic_keepalive() is True
             wr = tset()
@@ -264,11 +271,29 @@
             assert x2 is not None
             assert wr() is not None
             assert wr() is x2
+            return wr
+
+        def thread_entry_point():
+            wr = f()
+            wr_from_thread.wr = wr
+            wr_from_thread.seen = True
+
+        def main():
+            wr_from_thread.seen = False
+            start_new_thread(thread_entry_point, ())
+            wr1 = f()
+            time.sleep(0.5)
+            assert wr_from_thread.seen is True
+            wr2 = wr_from_thread.wr
+            import gc; gc.collect()      # wr2() should be collected here
+            assert wr1() is not None     # this thread, still running
+            assert wr2() is None         # other thread, not running any more
             return 42
 
-        for no__thread in (True, False):
-            if SUPPORT__THREAD or no__thread:
-                extra_options = {'no__thread': no__thread}
-                fn = self.getcompiled(f, [], extra_options=extra_options)
-                res = fn()
-                assert res == 42
+        extra_options = {'no__thread': no__thread}
+        fn = self.getcompiled(main, [], extra_options=extra_options)
+        res = fn()
+        assert res == 42
+
+    def test_tlref_keepalive__thread(self):
+        self.test_tlref_keepalive(no__thread=False)
diff --git a/rpython/translator/c/src/threadlocal.c 
b/rpython/translator/c/src/threadlocal.c
--- a/rpython/translator/c/src/threadlocal.c
+++ b/rpython/translator/c/src/threadlocal.c
@@ -3,12 +3,15 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#ifndef _WIN32
-# include <pthread.h>
-#endif
 #include "src/threadlocal.h"
 
 
+pthread_key_t pypy_threadlocal_key
+#ifdef _WIN32
+= TLS_OUT_OF_INDEXES
+#endif
+;
+
 static struct pypy_threadlocal_s linkedlist_head = {
     .prev = &linkedlist_head,
     .next = &linkedlist_head };
@@ -51,63 +54,62 @@
     tls->ready = 42;
 }
 
-static void threadloc_unlink(struct pypy_threadlocal_s *tls)
+static void threadloc_unlink(void *p)
 {
-    assert(tls->ready == 42);
-    tls->next->prev = tls->prev;
-    tls->prev->next = tls->next;
-    memset(tls, 0xDD, sizeof(struct pypy_threadlocal_s));  /* debug */
-    tls->ready = 0;
+    struct pypy_threadlocal_s *tls = (struct pypy_threadlocal_s *)p;
+    if (tls->ready == 42) {
+        tls->ready = 0;
+        tls->next->prev = tls->prev;
+        tls->prev->next = tls->next;
+        memset(tls, 0xDD, sizeof(struct pypy_threadlocal_s));  /* debug */
+    }
+#ifndef USE___THREAD
+    free(p);
+#endif
 }
 
-
-/* ------------------------------------------------------------ */
-#ifdef USE___THREAD
-/* ------------------------------------------------------------ */
-
-
-/* in this situation, we always have one full 'struct pypy_threadlocal_s'
-   available, managed by gcc. */
-__thread struct pypy_threadlocal_s pypy_threadlocal;
+#ifdef _WIN32
+/* xxx Defines a DllMain() function.  It's horrible imho: it only
+   works if we happen to compile a DLL (not a EXE); and of course you
+   get link-time errors if two files in the same DLL do the same.
+   There are some alternatives known, but they are horrible in other
+   ways (e.g. using undocumented behavior).  This seems to be the
+   simplest, but feel free to fix if you need that.
+ */
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,
+                    DWORD     reason_for_call,
+                    LPVOID    reserved)
+{
+    LPVOID p;
+    switch (reason_for_call) {
+    case DLL_THREAD_DETACH:
+        if (pypy_threadlocal_key != TLS_OUT_OF_INDEXES) {
+            p = TlsGetValue(pypy_threadlocal_key);
+            if (p != NULL) {
+                TlsSetValue(pypy_threadlocal_key, NULL);
+                threadloc_unlink(p);
+            }
+        }
+        break;
+    default:
+        break;
+    }
+    return TRUE;
+}
+#endif
 
 void RPython_ThreadLocals_ProgramInit(void)
 {
-    _RPy_ThreadLocals_Init(&pypy_threadlocal);
-}
-
-char *_RPython_ThreadLocals_Build(void)
-{
-    RPyAssert(pypy_threadlocal.ready == 0, "corrupted thread-local");
-    _RPy_ThreadLocals_Init(&pypy_threadlocal);
-    return (char *)&pypy_threadlocal;
-}
-
-void RPython_ThreadLocals_ThreadDie(void)
-{
-    if (pypy_threadlocal.ready == 42)
-        threadloc_unlink(&pypy_threadlocal);
-}
-
-
-/* ------------------------------------------------------------ */
-#else
-/* ------------------------------------------------------------ */
-
-
-/* this is the case where the 'struct pypy_threadlocal_s' is allocated
-   explicitly, with malloc()/free(), and attached to (a single) thread-
-   local key using the API of Windows or pthread. */
-
-pthread_key_t pypy_threadlocal_key;
-
-
-void RPython_ThreadLocals_ProgramInit(void)
-{
+    /* Initialize the pypy_threadlocal_key, together with a destructor
+       that will be called every time a thread shuts down (if there is
+       a non-null thread-local value).  This is needed even in the
+       case where we use '__thread' below, for the destructor.
+    */
 #ifdef _WIN32
     pypy_threadlocal_key = TlsAlloc();
     if (pypy_threadlocal_key == TLS_OUT_OF_INDEXES)
 #else
-    if (pthread_key_create(&pypy_threadlocal_key, NULL) != 0)
+    if (pthread_key_create(&pypy_threadlocal_key, threadloc_unlink) != 0)
 #endif
     {
         fprintf(stderr, "Internal RPython error: "
@@ -117,6 +119,45 @@
     _RPython_ThreadLocals_Build();
 }
 
+
+/* ------------------------------------------------------------ */
+#ifdef USE___THREAD
+/* ------------------------------------------------------------ */
+
+
+/* in this situation, we always have one full 'struct pypy_threadlocal_s'
+   available, managed by gcc. */
+__thread struct pypy_threadlocal_s pypy_threadlocal;
+
+char *_RPython_ThreadLocals_Build(void)
+{
+    RPyAssert(pypy_threadlocal.ready == 0, "corrupted thread-local");
+    _RPy_ThreadLocals_Init(&pypy_threadlocal);
+
+    /* we also set up &pypy_threadlocal as a POSIX thread-local variable,
+       because we need the destructor behavior. */
+    pthread_setspecific(pypy_threadlocal_key, (void *)&pypy_threadlocal);
+
+    return (char *)&pypy_threadlocal;
+}
+
+void RPython_ThreadLocals_ThreadDie(void)
+{
+    pthread_setspecific(pypy_threadlocal_key, NULL);
+    threadloc_unlink(&pypy_threadlocal);
+}
+
+
+/* ------------------------------------------------------------ */
+#else
+/* ------------------------------------------------------------ */
+
+
+/* this is the case where the 'struct pypy_threadlocal_s' is allocated
+   explicitly, with malloc()/free(), and attached to (a single) thread-
+   local key using the API of Windows or pthread. */
+
+
 char *_RPython_ThreadLocals_Build(void)
 {
     void *p = malloc(sizeof(struct pypy_threadlocal_s));
@@ -135,8 +176,7 @@
     void *p = _RPy_ThreadLocals_Get();
     if (p != NULL) {
         _RPy_ThreadLocals_Set(NULL);
-        threadloc_unlink((struct pypy_threadlocal_s *)p);
-        free(p);
+        threadloc_unlink(p);   /* includes free(p) */
     }
 }
 
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to