void __wait(volatile int *addr, volatile int *waiters, int val, int priv) { int spins=10000; if (priv) priv = 128; priv=0; while (spins--) { if (*addr==val) a_spin(); else return; } if (waiters) a_inc(waiters); while (*addr==val) { #ifdef __EMSCRIPTEN__ if (pthread_self()->cancelasync == PTHREAD_CANCEL_ASYNCHRONOUS) { // Must wait in slices in case this thread is cancelled in between. int e; do { if (_pthread_isduecanceled(pthread_self())) { if (waiters) a_dec(waiters); return; } e = emscripten_futex_wait((void*)addr, val, 100); } while(e == -ETIMEDOUT); } else { // Can wait in one go. emscripten_futex_wait((void*)addr, val, INFINITY); } #else __syscall(SYS_futex, addr, FUTEX_WAIT|priv, val, 0); #endif } if (waiters) a_dec(waiters); }
void __lock(volatile int *l) { int spins=100000; /* Do not use futexes because we insist that unlocking is a simple * assignment to optimize non-pathological code with no contention. */ while (a_xchg(l, 1)) if (spins) spins--, a_spin(); else __syscall(SYS_sched_yield); }
void __wait(volatile int *addr, volatile int *waiters, int val, int priv) { int spins=50000; if (priv) priv = 128; priv=0; while (spins--) { if (*addr==val) a_spin(); else return; } if (waiters) a_inc(waiters); while (*addr==val) __syscall(SYS_futex, (long)addr, FUTEX_WAIT|priv, val, 0); if (waiters) a_dec(waiters); }
int pthread_barrier_wait(pthread_barrier_t *b) { int limit = b->_b_limit; struct instance *inst; /* Trivial case: count was set at 1 */ if (!limit) return PTHREAD_BARRIER_SERIAL_THREAD; /* Process-shared barriers require a separate, inefficient wait */ if (limit < 0) return pshared_barrier_wait(b); /* Otherwise we need a lock on the barrier object */ while (a_swap(&b->_b_lock, 1)) __wait(&b->_b_lock, &b->_b_waiters, 1, 1); inst = b->_b_inst; /* First thread to enter the barrier becomes the "instance owner" */ if (!inst) { struct instance new_inst = { 0 }; int spins = 10000; b->_b_inst = inst = &new_inst; a_store(&b->_b_lock, 0); if (b->_b_waiters) __wake(&b->_b_lock, 1, 1); while (spins-- && !inst->finished) a_spin(); a_inc(&inst->finished); while (inst->finished == 1) __syscall(SYS_futex, &inst->finished, FUTEX_WAIT,1,0); return PTHREAD_BARRIER_SERIAL_THREAD; } /* Last thread to enter the barrier wakes all non-instance-owners */ if (++inst->count == limit) { b->_b_inst = 0; a_store(&b->_b_lock, 0); if (b->_b_waiters) __wake(&b->_b_lock, 1, 1); a_store(&inst->last, 1); if (inst->waiters) __wake(&inst->last, -1, 1); } else { a_store(&b->_b_lock, 0); if (b->_b_waiters) __wake(&b->_b_lock, 1, 1); __wait(&inst->last, &inst->waiters, 0, 1); } /* Last thread to exit the barrier wakes the instance owner */ if (a_fetch_add(&inst->count,-1)==1 && a_fetch_add(&inst->finished,1)) __wake(&inst->finished, 1, 1); return 0; }
int pthread_spin_lock(pthread_spinlock_t *s) { while (a_swap(s, 1)) a_spin(); return 0; }