Author: Armin Rigo <[email protected]>
Branch: fast-gil
Changeset: r69760:d548518bdb3f
Date: 2014-03-06 20:24 +0100
http://bitbucket.org/pypy/pypy/changeset/d548518bdb3f/
Log: Theoretical improvements
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,20 +485,15 @@
#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__)
- "lock xaddq %0, %1"
+# define atomic_add(ptr, value) asm volatile ("lock addq %0, %1" \
+ : : "ri"(value), "m"(*(ptr)) : "memory")
# elif defined(__i386__)
- "lock xaddl %0, %1"
+# define atomic_add(ptr, value) asm volatile ("lock addl %0, %1" \
+ : : "ri"(value), "m"(*(ptr)) : "memory")
# 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) \
@@ -583,6 +578,21 @@
#ifdef RPY_FASTGIL_VARNAME
#include <time.h>
+static inline void *atomic_xchg(void **ptr, void *value)
+{
+ void *result;
+ asm volatile (
+#if defined(__amd64__)
+ "xchgq %0, %1 /* automatically locked */"
+#elif defined(__i386__)
+ "xchgl %0, %1 /* automatically locked */"
+#else
+# error "RPY_FASTGIL_VARNAME: only for x86 right now"
+#endif
+ : "r"(result) : "0"(value), "m"(*ptr) : "memory");
+ return result;
+}
+
static inline timespec_add(struct timespec *t, unsigned long long incr)
{
unsigned long long nsec = t->tv_nsec + incr;
@@ -593,7 +603,7 @@
t->tv_nsec = (long)nsec;
}
-static inline void _acquire_gil_or_wait_for_fastgil_to_be_one(void)
+static inline void _acquire_gil_or_wait_for_fastgil_to_be_nonzero(void)
{
/* Support for the JIT, which generates calls to external C
functions using the following very fast pattern:
@@ -602,14 +612,27 @@
real variable) contains normally 0
* before doing an external C call, the generated assembler sets
- it to 1
+ this global variable to an in-stack pointer to its
+ ASM_FRAMEDATA_HEAD structure (for asmgcc) or to 1 (for
+ shadowstack, when implemented)
- * afterwards, it uses an atomic instruction to decrement it,
- and if it goes back to 0, everything is fine
+ * afterwards, it uses an atomic instruction to get the current
+ value stored in the variable and to replace it with zero
- * otherwise, someone else (this function actually) stole the
- GIL. The assembler needs to call RPyGilAcquire() again.
-
+ * if the old value was still the ASM_FRAMEDATA_HEAD pointer of
+ this thread, everything is fine
+
+ * otherwise, someone else stole the GIL. The assembler calls a
+ helper. This helper first needs to unlink this thread's
+ ASM_FRAMEDATA_HEAD from the chained list where it was put by
+ the stealing code. If the old value was zero, it means that
+ the stealing code was this function here. In that case, the
+ helper needs to call RPyGilAcquire() again. If, on the other
+ hand, the old value is another ASM_FRAMEDATA_HEAD from a
+ different thread, it means we just stole the fast GIL from this
+ other thread. In that case we store that different
+ ASM_FRAMEDATA_HEAD into the chained list and return immediately.
+
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,
@@ -627,17 +650,25 @@
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;
- }
+ void *fastgilvalue;
+ fastgilvalue = atomic_xchg(&RPY_FASTGIL_VARNAME, NULL);
+ if (fastgilvalue != NULL) {
+ /* 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. We only need to fix
+ the asmgcc linked list.
+ */
+ struct pypy_ASM_FRAMEDATA_HEAD0 *new =
+ (struct pypy_ASM_FRAMEDATA_HEAD0 *)fastgilvalue;
+ struct pypy_ASM_FRAMEDATA_HEAD0 *root = &pypy_g_ASM_FRAMEDATA_HEAD;
+ struct pypy_ASM_FRAMEDATA_HEAD0 *next = root->as_next;
+ new->as_next = next;
+ new->as_prev = root;
+ root->as_next = new;
+ next->as_prev = new;
+ return;
}
/* sleep for a bit of time */
@@ -670,7 +701,7 @@
}
atomic_add(&pending_acquires, 1L);
#ifdef RPY_FASTGIL_VARNAME
- _acquire_gil_or_wait_for_fastgil_to_be_zero();
+ _acquire_gil_or_wait_for_fastgil_to_be_nonzero();
#else
ASSERT_STATUS(pthread_mutex_lock(&mutex_gil));
#endif
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit