void tlTaskStart(tlTask* task) { trace("%s", tl_str(task)); assert(tlTaskIs(task)); assert(task->state == TL_STATE_INIT); assert(task->stack); tlVm* vm = tlTaskGetVm(task); a_inc(&vm->tasks); a_inc(&vm->runnable); task->worker = null; task->state = TL_STATE_READY; lqueue_put(&vm->run_q, &task->entry); }
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); }
tlTask* tlTaskNew(tlVm* vm, tlObject* locals) { tlTask* task = tlAlloc(tlTaskKind, sizeof(tlTask)); assert(task->state == TL_STATE_INIT); task->id = a_inc(&vm->nexttaskid); task->worker = vm->waiter; task->locals = locals; task->ticks = START_TICKS; trace("new %s", tl_str(task)); return task; }
int pthread_cond_broadcast(pthread_cond_t *c) { pthread_mutex_t *m; if (!c->_c_waiters) return 0; a_inc(&c->_c_seq); #ifdef __EMSCRIPTEN__ // XXX Emscripten: TODO: This is suboptimal but works naively correctly for now. The Emscripten-specific code path below // has a bug and does not work for some reason. Figure it out and remove this code block. __wake(&c->_c_seq, -1, 0); return 0; #endif /* If cond var is process-shared, simply wake all waiters. */ if (c->_c_mutex == (void *)-1) { __wake(&c->_c_seq, -1, 0); return 0; } /* Block waiters from returning so we can use the mutex. */ while (a_swap(&c->_c_lock, 1)) __wait(&c->_c_lock, &c->_c_lockwait, 1, 1); if (!c->_c_waiters) goto out; m = c->_c_mutex; /* Move waiter count to the mutex */ a_fetch_add(&m->_m_waiters, c->_c_waiters2); c->_c_waiters2 = 0; #ifdef __EMSCRIPTEN__ int futexResult; do { // XXX Emscripten: Bug, this does not work correctly. futexResult = emscripten_futex_wake_or_requeue(&c->_c_seq, !m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid, c->_c_seq, &m->_m_lock); } while(futexResult == -EAGAIN); #else /* Perform the futex requeue, waking one waiter unless we know * that the calling thread holds the mutex. */ __syscall(SYS_futex, &c->_c_seq, FUTEX_REQUEUE, !m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid, INT_MAX, &m->_m_lock); #endif out: a_store(&c->_c_lock, 0); if (c->_c_lockwait) __wake(&c->_c_lock, 1, 0); return 0; }
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_mutex_timedlock(pthread_mutex_t *m, const struct timespec *at) { int r, w=0; while ((r=pthread_mutex_trylock(m)) == EBUSY) { if (!(r=m->_m_lock) || (r&0x40000000)) continue; if (!w) a_inc(&m->_m_waiters), w++; if (__timedwait(&m->_m_lock, r, CLOCK_REALTIME, at, 0) == ETIMEDOUT) { if (w) a_dec(&m->_m_waiters); return ETIMEDOUT; } } if (w) a_dec(&m->_m_waiters); return r; }
int sem_timedwait(sem_t *sem, const struct timespec *at) { while (sem_trywait(sem)) { int r; a_inc(sem->__val+1); a_cas(sem->__val, 0, -1); r = __timedwait(sem->__val, -1, CLOCK_REALTIME, at, cleanup, sem->__val+1, 0); a_dec(sem->__val+1); if (r) { errno = r; return -1; } } return 0; }
void tlTaskReady(tlTask* task) { trace("%s.value: %s", tl_str(task), tl_str(task->value)); assert(tlTaskIs(task)); assert(task->state == TL_STATE_WAIT); assert(task->stack); tlVm* vm = tlTaskGetVm(task); task->waitFor = null; a_inc(&vm->runnable); task->state = TL_STATE_READY; if (tlWorkerIsBound(task->worker)) { tlWorkerSignal(task->worker); } else { task->worker = null; lqueue_put(&vm->run_q, &task->entry); } }
int pthread_mutex_timedlock(pthread_mutex_t *m, const struct timespec *at) { int r, t; if (m->_m_type == PTHREAD_MUTEX_NORMAL && !a_cas(&m->_m_lock, 0, EBUSY)) return 0; while ((r=pthread_mutex_trylock(m)) == EBUSY) { if (!(r=m->_m_lock) || (r&0x40000000)) continue; if ((m->_m_type&3) == PTHREAD_MUTEX_ERRORCHECK && (r&0x1fffffff) == pthread_self()->tid) return EDEADLK; a_inc(&m->_m_waiters); t = r | 0x80000000; a_cas(&m->_m_lock, r, t); r = __timedwait(&m->_m_lock, t, CLOCK_REALTIME, at, 0, 0, 0); a_dec(&m->_m_waiters); if (r && r != EINTR) break; } return r; }
int pthread_cond_timedwait(pthread_cond_t *c, pthread_mutex_t *m, const struct timespec *ts) { struct cm cm = { .c=c, .m=m }; int r, e=0, seq; if (m->_m_type && (m->_m_lock&INT_MAX) != pthread_self()->tid) return EPERM; if (ts && ts->tv_nsec >= 1000000000UL) return EINVAL; pthread_testcancel(); a_inc(&c->_c_waiters); if (c->_c_mutex != (void *)-1) { c->_c_mutex = m; while (a_swap(&c->_c_lock, 1)) __wait(&c->_c_lock, &c->_c_lockwait, 1, 1); c->_c_waiters2++; a_store(&c->_c_lock, 0); if (c->_c_lockwait) __wake(&c->_c_lock, 1, 1); } seq = c->_c_seq; pthread_mutex_unlock(m); do e = __timedwait(&c->_c_seq, seq, c->_c_clock, ts, cleanup, &cm, 0); while (c->_c_seq == seq && (!e || e==EINTR)); if (e == EINTR) e = 0; unwait(c, m); if ((r=pthread_mutex_lock(m))) return r; return e; }
int pthread_cond_broadcast(pthread_cond_t *c) { pthread_mutex_t *m; if (!c->_c_waiters) return 0; a_inc(&c->_c_seq); /* If cond var is process-shared, simply wake all waiters. */ if (c->_c_mutex == (void *)-1) { __wake(&c->_c_seq, -1, 0); return 0; } /* Block waiters from returning so we can use the mutex. */ while (a_swap(&c->_c_lock, 1)) __wait(&c->_c_lock, &c->_c_lockwait, 1, 1); if (!c->_c_waiters) goto out; m = c->_c_mutex; /* Move waiter count to the mutex */ a_fetch_add(&m->_m_waiters, c->_c_waiters2); c->_c_waiters2 = 0; /* Perform the futex requeue, waking one waiter unless we know * that the calling thread holds the mutex. */ __syscall(SYS_futex, &c->_c_seq, FUTEX_REQUEUE, !m->_m_type || (m->_m_lock&INT_MAX)!=pthread_self()->tid, INT_MAX, &m->_m_lock); out: a_store(&c->_c_lock, 0); if (c->_c_lockwait) __wake(&c->_c_lock, 1, 0); return 0; }
tlTask* tlTaskWaitExternal(tlTask* task) { tlVm* vm = tlTaskGetVm(task); if (!task->background) a_inc(&vm->waitevent); tlTaskWaitFor(task, null); return task; }
void __aio_wake(void) { a_inc(&seq); __wake(&seq, -1, 1); }
void __vm_lock() { a_inc(vmlock); }
void tlVmIncExternal(tlVm* vm) { assert(vm); a_inc(&vm->waitevent); }