Author: Armin Rigo <[email protected]>
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
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit