[pypy-commit] pypy fast-gil: Change rpy_fastgil (invert the 0 and 1 values) to be more consistent
arigo
noreply at buildbot.pypy.org
Tue Jun 24 12:13:45 CEST 2014
Author: Armin Rigo <arigo at tunes.org>
Branch: fast-gil
Changeset: r72184:206fd7ee9cc1
Date: 2014-06-24 11:38 +0200
http://bitbucket.org/pypy/pypy/changeset/206fd7ee9cc1/
Log: Change rpy_fastgil (invert the 0 and 1 values) to be more consistent
with the standards of __sync_lock_test_and_set().
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
@@ -28,13 +28,13 @@
void RPyGilYieldThread(void);
void RPyGilAcquire(void);
-extern void *rpy_fastgil;
+extern Signed rpy_fastgil;
static inline void RPyGilRelease(void) {
- assert(rpy_fastgil == NULL);
- rpy_fastgil = (void *)1;
+ assert(RPY_FASTGIL_HELD(rpy_fastgil));
+ rpy_fastgil = 0;
}
-static inline void *RPyFetchFastGil(void) {
+static inline Signed *RPyFetchFastGil(void) {
return &rpy_fastgil;
}
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
@@ -484,35 +484,48 @@
/* Idea:
- - "The GIL" is a composite concept. "The GIL is locked" means
- that the global variable 'rpy_fastgil' is zero *and* the
- 'mutex_gil' is acquired. Conversely, "the GIL is unlocked" means
- that rpy_fastgil != 0 *or* mutex_gil is released. It should never
- be the case that these two conditions are true at the same time.
+ - "The GIL" is a composite concept. There are two locks, and "the
+ GIL is locked" when both are locked.
+
+ - The first lock is a simple global variable 'rpy_fastgil'. With
+ shadowstack, we use the most portable definition: 0 means unlocked
+ and != 0 means locked. With asmgcc, 0 means unlocked but only 1
+ means locked. A different value means unlocked too, but the value
+ is used by the JIT to contain the stack top for stack root scanning.
+
+ - The second lock is a regular mutex. In the fast path, it is never
+ unlocked. Remember that "the GIL is unlocked" means that either
+ the first or the second lock is unlocked. It should never be the
+ case that both are unlocked at the same time.
- Let's call "thread 1" the thread with the GIL. Whenever it does an
- external function call, it sets 'rpy_fastgil' to a non-null value.
+ external function call, it sets 'rpy_fastgil' to 0 (unlocked).
This is the cheapest way to release the GIL. When it returns from
- the function call, this thread attempts to atomically reset
- rpy_fastgil to zero. In the common case where it works, thread 1
+ the function call, this thread attempts to atomically change
+ 'rpy_fastgil' to 1. In the common case where it works, thread 1
has got the GIL back and so continues to run.
- - But "thread 2" is eagerly waiting for thread 1 to become blocked in
- some long-running call. About every millisecond it checks if
- 'rpy_fastgil' is non-null, by atomically resetting it to zero.
- If it was non-null, it means that the GIL was not actually locked,
- and thread 2 has now got the GIL.
+ - Say "thread 2" is eagerly waiting for thread 1 to become blocked in
+ some long-running call. Regularly, it checks if 'rpy_fastgil' is 0
+ and tries to atomically change it to 1. If it succeeds, it means
+ that the GIL was not previously locked. Thread 2 has now got the GIL.
- - If there are more threads, they are really sleeping, waiting on the
- 'mutex_gil_stealer' held by thread 2.
+ - If there are more than 2 threads, the rest is really sleeping by
+ waiting on the 'mutex_gil_stealer' held by thread 2.
- An additional mechanism is used for when thread 1 wants to
explicitly yield the GIL to thread 2: it does so by releasing
'mutex_gil' (which is otherwise not released) but keeping the
- value of 'rpy_fastgil' to zero.
+ value of 'rpy_fastgil' to 1.
*/
-void *rpy_fastgil = NULL;
+#ifdef PYPY_USE_ASMGCC
+# define RPY_FASTGIL_LOCKED(x) (x == 1)
+#else
+# define RPY_FASTGIL_LOCKED(x) (x != 0)
+#endif
+
+Signed rpy_fastgil = 1;
static pthread_mutex_t mutex_gil_stealer;
static pthread_mutex_t mutex_gil;
static pthread_once_t mutex_gil_once = PTHREAD_ONCE_INIT;
@@ -530,26 +543,6 @@
pthread_once(&mutex_gil_once, &init_mutex_gil);
}
-static inline void *atomic_xchg(void **ptr, void *value)
-{
- void *result;
-#if defined(__amd64__)
- asm volatile ("xchgq %0, %2 /* automatically locked */"
- : "=r"(result) : "0"(value), "m"(*ptr) : "memory");
-#elif defined(__i386__)
- asm volatile ("xchgl %0, %2 /* automatically locked */"
- : "=r"(result) : "0"(value), "m"(*ptr) : "memory");
-#else
- /* requires gcc >= 4.1 */
- while (1) {
- result = *ptr;
- if (__sync_bool_compare_and_swap(ptr, result, value))
- break;
- }
-#endif
- return result;
-}
-
static inline void timespec_add(struct timespec *t, long incr)
{
long nsec = t->tv_nsec + incr;
@@ -565,12 +558,11 @@
{
/* Acquires the GIL. Note: this function saves and restores 'errno'.
*/
- void *old_fastgil = atomic_xchg(&rpy_fastgil, NULL);
+ Signed old_fastgil = __sync_lock_test_and_set(&rpy_fastgil, 1);
- if (old_fastgil != NULL) {
- /* If we get a non-NULL value, it means that no other thread had the
- GIL, and the exchange was successful. 'mutex_gil' should still
- be locked at this point.
+ if (!RPY_FASTGIL_LOCKED(old_fastgil)) {
+ /* The fastgil was not previously locked: success.
+ 'mutex_gil' should still be locked at this point.
*/
}
else {
@@ -602,29 +594,29 @@
/* We arrive here if 'mutex_gil' was recently released
and we just relocked it.
*/
- assert(rpy_fastgil == NULL);
- old_fastgil = (void *)1;
+ old_fastgil = 0;
break;
}
/* Busy-looping here. Try to look again if 'rpy_fastgil' is
- non-NULL.
+ released.
*/
- if (rpy_fastgil != NULL) {
- old_fastgil = atomic_xchg(&rpy_fastgil, NULL);
- if (old_fastgil != NULL) {
- /* yes, got a non-NULL value! */
+ if (!RPY_FASTGIL_LOCKED(rpy_fastgil)) {
+ old_fastgil = __sync_lock_test_and_set(&rpy_fastgil, 1);
+ if (!RPY_FASTGIL_LOCKED(old_fastgil))
+ /* yes, got a non-held value! Now we hold it. */
break;
}
}
/* Otherwise, loop back. */
}
+ ASSERT_STATUS(pthread_mutex_unlock(&mutex_gil_stealer));
errno = old_errno;
}
#ifdef PYPY_USE_ASMGCC
- if (old_fastgil != (void *)1) {
+ if (old_fastgil != 0) {
/* this case only occurs from the JIT compiler */
struct pypy_ASM_FRAMEDATA_HEAD0 *new =
(struct pypy_ASM_FRAMEDATA_HEAD0 *)old_fastgil;
@@ -636,8 +628,9 @@
next->as_prev = new;
}
#else
- assert(old_fastgil == (void *)1);
+ assert(old_fastgil == 0);
#endif
+ assert(RPY_FASTGIL_LOCKED(rpy_fastgil));
return;
}
@@ -654,7 +647,7 @@
void RPyGilYieldThread(void)
{
- assert(rpy_fastgil == NULL);
+ assert(RPY_FASTGIL_LOCKED(rpy_fastgil));
/* Explicitly release the 'mutex_gil'.
*/
@@ -662,7 +655,7 @@
ASSERT_STATUS(pthread_mutex_unlock(&mutex_gil));
/* Now nobody has got the GIL, because 'mutex_gil' is released (but
- rpy_fastgil is still zero). Call RPyGilAcquire(). It will
+ rpy_fastgil is still locked). Call RPyGilAcquire(). It will
enqueue ourselves at the end of the 'mutex_gil_stealer' queue.
If there is no other waiting thread, it will fall through both
its pthread_mutex_lock() and pthread_mutex_timedlock() now.
More information about the pypy-commit
mailing list