[pypy-commit] pypy fast-gil: Untested, but the idea would be to have this kind of code.

arigo noreply at buildbot.pypy.org
Thu Mar 6 19:16:15 CET 2014


Author: Armin Rigo <arigo at tunes.org>
Branch: fast-gil
Changeset: r69758:ff24b8da9ce6
Date: 2014-03-06 19:15 +0100
http://bitbucket.org/pypy/pypy/changeset/ff24b8da9ce6/

Log:	Untested, but the idea would be to have this kind of code.

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
@@ -485,15 +485,20 @@
 #ifdef HAS_ATOMIC_ADD
 #  define atomic_add __sync_fetch_and_add
 #else
+static inline long atomic_add(long *ptr, long value)
+{
+    long result;
+    asm volatile (
 #  if defined(__amd64__)
-#    define atomic_add(ptr, value)  asm volatile ("lock addq %0, %1"        \
-                                 : : "ri"(value), "m"(*(ptr)) : "memory")
+                  "lock xaddq %0, %1"
 #  elif defined(__i386__)
-#    define atomic_add(ptr, value)  asm volatile ("lock addl %0, %1"        \
-                                 : : "ri"(value), "m"(*(ptr)) : "memory")
+                  "lock xaddl %0, %1"
 #  else
 #    error "Please use gcc >= 4.1 or write a custom 'asm' for your CPU."
 #  endif
+                  : "=r"(result) : "0"(value), "m"(*ptr) : "memory");
+    return result;
+}
 #endif
 
 #define ASSERT_STATUS(call)                             \
@@ -575,14 +580,100 @@
     ASSERT_STATUS(pthread_cond_signal(&cond_gil));
 }
 
+#ifdef RPY_FASTGIL_VARNAME
+#include <time.h>
+
+static inline timespec_add(struct timespec *t, unsigned long long incr)
+{
+    unsigned long long nsec = t->tv_nsec + incr;
+    if (nsec >= 1000000000) {
+        t->tv_sec += (nsec / 1000000000);
+        nsec %= 1000000000;
+    }
+    t->tv_nsec = (long)nsec;
+}
+
+static inline void _acquire_gil_or_wait_for_fastgil_to_be_one(void)
+{
+    /* Support for the JIT, which generates calls to external C
+       functions using the following very fast pattern:
+
+       * the global variable 'RPY_FASTGIL_VARNAME' (a macro naming the
+         real variable) contains normally 0
+
+       * before doing an external C call, the generated assembler sets
+         it to 1
+
+       * afterwards, it uses an atomic instruction to decrement it,
+         and if it goes back to 0, everything is fine
+
+       * otherwise, someone else (this function actually) stole the
+         GIL.  The assembler needs to call RPyGilAcquire() again.
+    
+       This function is a balancing act inspired by CPython 2.7's
+       threading.py for _Condition.wait() (not the PyPy version, which
+       was modified).  We need to wait for the real GIL to be released,
+       but also notice if the fast GIL contains 1.  We can't afford a
+       pure busy loop, so we have to sleep; but if we just sleep until
+       the real GIL is released, we won't ever see the fast GIL being 1.
+       The scheme here sleeps very little at first, and longer as time
+       goes on.  Eventually, the real GIL should be released, so there
+       is no point in trying to bound the maximal length of the wait.
+    */
+    unsigned long long delay = 100000; /* in ns; initial delay is 0.1 ms */
+    struct timespec t;
+    clock_gettime(CLOCK_REALTIME, &t);
+
+    while (1) {
+
+        /* try to see if we can steal the fast GIL */
+        if (RPY_FASTGIL_VARNAME == 1) {
+            if (atomic_add(&RPY_FASTGIL_VARNAME, -1) == 1) {
+                /* yes, succeeded.  We know that the other thread is
+                   before the return to JITted assembler from the C
+                   function call.  The JITted assembler will definitely
+                   call RPyGilAcquire() then.  So we can just pretend
+                   that the GIL --- which is still acquired --- is ours
+                   now.
+                */
+                return;
+            }
+        }
+
+        /* sleep for a bit of time */
+        timespec_add(&t, delay);
+        int error = pthread_mutex_timedlock(&mutex_gil, &t);
+
+        if (error == ETIMEDOUT) {
+            delay = (delay * 3) / 2;
+            continue;
+        }
+        else {
+            ASSERT_STATUS(error);
+            /* succeeded in acquiring the real GIL */
+            return;
+        }
+    }
+}
+#endif
+
 void RPyGilAcquire(void)
 {
     _debug_print("about to RPyGilAcquire...\n");
 #ifdef RPY_ASSERT
     assert(pending_acquires >= 0);
 #endif
+    if (pthread_mutex_trylock(&mutex_gil) == 0) {
+        assert_has_the_gil();
+        _debug_print("got it without waiting\n");
+        return;
+    }
     atomic_add(&pending_acquires, 1L);
+#ifdef RPY_FASTGIL_VARNAME
+    _acquire_gil_or_wait_for_fastgil_to_be_zero();
+#else
     ASSERT_STATUS(pthread_mutex_lock(&mutex_gil));
+#endif
     atomic_add(&pending_acquires, -1L);
     assert_has_the_gil();
     _debug_print("RPyGilAcquire\n");


More information about the pypy-commit mailing list