[pypy-commit] pypy nogil-unsafe-2: add threadsan target and make tracebacks threadlocal

Raemi pypy.commits at gmail.com
Thu Mar 23 05:53:43 EDT 2017


Author: Remi Meier <remi.meier at gmail.com>
Branch: nogil-unsafe-2
Changeset: r90793:fb471557c36c
Date: 2017-03-23 10:53 +0100
http://bitbucket.org/pypy/pypy/changeset/fb471557c36c/

Log:	add threadsan target and make tracebacks threadlocal

	Add support for gcc's thread sanitizer by adding a makefile target.
	Avoid some data races by making the traceback buffer thread-local.

diff --git a/rpython/config/translationoption.py b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -198,6 +198,9 @@
     BoolOption("lldebug0",
                "If true, makes an lldebug0 build", default=False,
                cmdline="--lldebug0"),
+    BoolOption("threadsan",
+               "If true, makes an thread-sanitizer build", default=False,
+               cmdline="--threadsan"),
     StrOption("icon", "Path to the (Windows) icon to use for the executable"),
     StrOption("libname",
               "Windows: name and possibly location of the lib file to create"),
diff --git a/rpython/translator/c/genc.py b/rpython/translator/c/genc.py
--- a/rpython/translator/c/genc.py
+++ b/rpython/translator/c/genc.py
@@ -397,6 +397,8 @@
             extra_opts += ["lldebug"]
         elif self.config.translation.lldebug0:
             extra_opts += ["lldebug0"]
+        elif self.config.translation.threadsan:
+            extra_opts += ["threadsan"]
         self.translator.platform.execute_makefile(self.targetdir,
                                                   extra_opts)
         if shared:
@@ -433,6 +435,9 @@
             ('llsafer', '', '$(MAKE) CFLAGS="-O2 -DRPY_LL_ASSERT" $(DEFAULT_TARGET)'),
             ('lldebug', '', '$(MAKE) CFLAGS="$(DEBUGFLAGS) -DRPY_ASSERT -DRPY_LL_ASSERT" debug_target'),
             ('profile', '', '$(MAKE) CFLAGS="-g -O1 -pg $(CFLAGS) -fno-omit-frame-pointer" LDFLAGS="-pg $(LDFLAGS)" $(DEFAULT_TARGET)'),
+            ('threadsan', '',
+             '$(MAKE) CFLAGS="-g -O1 $(CFLAGS) -fno-omit-frame-pointer -fsanitize=thread" '
+             + 'LDFLAGS="-g $(LDFLAGS) -fsanitize=thread" $(DEFAULT_TARGET)'),
             ]
         if self.has_profopt():
             rules.append(
diff --git a/rpython/translator/c/src/debug_traceback.c b/rpython/translator/c/src/debug_traceback.c
--- a/rpython/translator/c/src/debug_traceback.c
+++ b/rpython/translator/c/src/debug_traceback.c
@@ -6,8 +6,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-int pypydtcount = 0;
-struct pypydtentry_s pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
+__thread int pypydtcount = 0;
+__thread struct pypydtentry_s pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
 
 void pypy_debug_traceback_print(void)
 {
diff --git a/rpython/translator/c/src/debug_traceback.h b/rpython/translator/c/src/debug_traceback.h
--- a/rpython/translator/c/src/debug_traceback.h
+++ b/rpython/translator/c/src/debug_traceback.h
@@ -60,8 +60,8 @@
   void *exctype;
 };
 
-RPY_EXTERN int pypydtcount;
-RPY_EXTERN struct pypydtentry_s pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
+RPY_EXTERN __thread int pypydtcount;
+RPY_EXTERN __thread struct pypydtentry_s pypy_debug_tracebacks[PYPY_DEBUG_TRACEBACK_DEPTH];
 
 RPY_EXTERN void pypy_debug_traceback_print(void);
 RPY_EXTERN void pypy_debug_catch_fatal_exception(void);
diff --git a/rpython/translator/c/src/thread.h b/rpython/translator/c/src/thread.h
--- a/rpython/translator/c/src/thread.h
+++ b/rpython/translator/c/src/thread.h
@@ -76,17 +76,20 @@
  */
 
 #define _RPyGilAcquire() do {                                           \
-        assert((RPY_THREADLOCALREF_GET(synclock) & 0b001) == 0b0);      \
+        assert((__sync_fetch_and_add(                                   \
+                    &RPY_THREADLOCALREF_GET(synclock), 0)               \
+                & 0b001) == 0b0);                                       \
     if (!__sync_bool_compare_and_swap(                                  \
-                &RPY_THREADLOCALREF_GET(synclock), 0b100L, 0b101L))     \
+            &RPY_THREADLOCALREF_GET(synclock), 0b100L, 0b101L))         \
         RPyGilAcquireSlowPath();                                        \
-        } while (0)
+            } while (0)
 
-#define _RPyGilRelease() do {                                       \
-        assert((RPY_THREADLOCALREF_GET(synclock) & 0b101) == 0b101);  \
-    if (!__sync_bool_compare_and_swap(                              \
-                &RPY_THREADLOCALREF_GET(synclock), 0b101L, 0b100L)) \
-        RPyGilReleaseSlowPath();                                    \
+#define _RPyGilRelease() do {                                           \
+        assert((__sync_fetch_and_add(                                   \
+                    &RPY_THREADLOCALREF_GET(synclock), 0) & 0b101) == 0b101); \
+    if (!__sync_bool_compare_and_swap(                                  \
+            &RPY_THREADLOCALREF_GET(synclock), 0b101L, 0b100L))         \
+        RPyGilReleaseSlowPath();                                        \
         } while (0)
 
 static inline long *_RPyFetchFastGil(void) {
@@ -94,12 +97,12 @@
 //    return &rpy_fastgil;
 }
 
-#define RPyGilYieldThread() do { \
-    assert(RPY_THREADLOCALREF_GET(synclock) & 1L); \
-    if (RPY_THREADLOCALREF_GET(synclock) == 0b111L) { \
-        RPyGilYieldThreadSlowPath(); \
-    } \
-    } while (0)
+#define RPyGilYieldThread() do {                                        \
+        assert(__sync_fetch_and_add(&RPY_THREADLOCALREF_GET(synclock), 0) & 1L); \
+    if (RPY_THREADLOCALREF_GET(synclock) == 0b111L) {                   \
+        RPyGilYieldThreadSlowPath();                                    \
+            }                                                           \
+            } while (0)
 
 typedef unsigned char rpy_spinlock_t;
 static inline void rpy_spinlock_acquire(rpy_spinlock_t *p)
diff --git a/rpython/translator/c/src/thread_gil.c b/rpython/translator/c/src/thread_gil.c
--- a/rpython/translator/c/src/thread_gil.c
+++ b/rpython/translator/c/src/thread_gil.c
@@ -97,6 +97,7 @@
     RPyGilAcquire();
 }
 
+__attribute__((no_sanitize_thread))
 void RPyGilMasterRequestSafepoint(void)
 {
     pthread_mutex_lock(&sync_mutex);
@@ -111,10 +112,13 @@
         if (t == NULL)
             break;
 
-      retry:
-        switch (t->synclock) {
+      retry:;
+        /* this read and the setting of nursery_top make thread sanitizer
+         * unhappy */
+        long synclock = t->synclock;
+        switch (synclock) {
         default:
-            fprintf(stderr, "ERROR: found synclock=%ld\n", t->synclock);
+            fprintf(stderr, "ERROR: found synclock=%ld\n", synclock);
             abort();
         case 0b000L:
             /* new thread, no need to explicitly request safepoint */
diff --git a/rpython/translator/c/test/test_nogil.py b/rpython/translator/c/test/test_nogil.py
--- a/rpython/translator/c/test/test_nogil.py
+++ b/rpython/translator/c/test/test_nogil.py
@@ -22,6 +22,7 @@
         t.config.translation.gcrootfinder = self.gcrootfinder
         t.config.translation.thread = True
         t.config.translation.no__thread = no__thread
+        # t.config.translation.threadsan = True
         t.buildannotator().build_types(entry_point, [s_list_of_strings])
         t.buildrtyper().specialize()
         #
@@ -46,7 +47,7 @@
             pass
         state = State()
 
-        def thread():
+        def worker():
             rthread.gc_thread_start()
             x = None
             for i in range(100000000):
@@ -74,16 +75,16 @@
             state.counter = TS
 
             for _ in range(TS):
-                rthread.start_new_thread(thread, ())
+                rthread.start_new_thread(worker, ())
 
-            i = 0
             while True:
-                x = X(None, i)
                 time.sleep(0.1)
-                assert x.i == i
+                state.lock.acquire(True)
                 if state.counter == 0:
+                    state.lock.release()
                     break
-                i += 1
+                state.lock.release()
+
             os.write(1, "all threads done\n")
             return 0
 
@@ -152,8 +153,11 @@
 
             while True:
                 time.sleep(0.1)
+                state.lock.acquire(True)
                 if state.counter == 0:
+                    state.lock.release()
                     break
+                state.lock.release()
             os.write(1, "all threads done\n")
             return 0
 


More information about the pypy-commit mailing list