int futex_wait(fsem_t* p, const timespec* end_time) { int err = 0; timespec remain; if (decrement_if_positive(&p->val_) > 0) {} else { __sync_fetch_and_add(&p->nwaiters_, 1); while(1) { calc_remain_timespec(&remain, end_time); if (remain.tv_sec < 0) { err = ETIMEDOUT; break; } if (0 != futex(&p->val_, FUTEX_WAIT, 0, &remain, NULL, 0)) { err = errno; } if (0 != err && EWOULDBLOCK != err && EINTR != err) { break; } if (decrement_if_positive(&p->val_) > 0) { err = 0; break; } } __sync_fetch_and_add(&p->nwaiters_, -1); } return err; }
void shared_mutex::imp_wait() { #ifdef _WIN32 NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr); #else while (true) { // Load new value, try to acquire c_sig auto [value, ok] = m_value.fetch_op([](u32& value) { if (value >= c_sig) { value -= c_sig; return true; } return false; }); if (ok) { return; } futex(reinterpret_cast<int*>(&m_value.raw()), FUTEX_WAIT_BITSET_PRIVATE, value, nullptr, nullptr, c_sig); } #endif }
// Thread spamming us with signals void *sig_thread(void *arg) { int *done = (int*)arg; struct sigaction sigact = {.sa_handler = sig_handler, 0}; sigaction(SIGUSR1, &sigact, 0); while(1) { kill(getpid(), SIGUSR1); cmb(); if (*done) return NULL; } } int main(int argc, char **argv) { int done = false; pthread_t pth_handle, pth_handle2; // Spawn off a thread to spam us with signals pthread_create(&pth_handle, NULL, &sig_thread, &done); // Spawn off a thread to process those signals pthread_create(&pth_handle2, NULL, &count_signals, NULL); // Sleep for 3 seconds by timing out on a futex int dummy = 0; struct timespec timeout = {.tv_sec = 3, 0}; futex(&dummy, FUTEX_WAIT, 0, &timeout, NULL, 0); // Force the signal tread to exit cmb(); done = true; pthread_join(pth_handle, NULL); printf("count: %d\n", count); return 0; }
void *count_signals(void *arg) { while(1) { futex(&__sigpending, FUTEX_WAIT, 0, NULL, NULL, 0); __sync_fetch_and_add(&count, 1); __sigpending = 0; } }
int futex_wait(fsem_t* p) { int err = 0; if (decrement_if_positive(&p->val_) > 0) {} else { __sync_fetch_and_add(&p->nwaiters_, 1); while(1) { if (0 != futex(&p->val_, FUTEX_WAIT, 0, NULL, NULL, 0)) { err = errno; } if (0 != err && EWOULDBLOCK != err && EINTR != err) { break; } if (decrement_if_positive(&p->val_) > 0) { err = 0; break; } } __sync_fetch_and_add(&p->nwaiters_, -1); } return err; }
// Atomically, // if(*addr == val) sleep // Might be woken up spuriously; that's allowed. static void futexsleep(uint32 *addr, uint32 val) { // Some Linux kernels have a bug where futex of // FUTEX_WAIT returns an internal error code // as an errno. Libpthread ignores the return value // here, and so can we: as it says a few lines up, // spurious wakeups are allowed. futex(addr, FUTEX_WAIT, val, &longtime, nil, 0); }
void ThreadEntry::Wait(int value) { timespec ts; ts.tv_sec = 10; ts.tv_nsec = 0; errno = 0; futex(&futex_, FUTEX_WAIT, value, &ts, NULL, 0); if (errno != 0 && errno != EWOULDBLOCK) { BACK_LOGW("futex wait failed, futex = %d: %s", futex_, strerror(errno)); } }
void shared_mutex::imp_signal() { #ifdef _WIN32 NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr); #else m_value += c_sig; futex(reinterpret_cast<int*>(&m_value.raw()), FUTEX_WAKE_BITSET_PRIVATE, 1, nullptr, nullptr, c_sig); //futex(reinterpret_cast<int*>(&m_value.raw()), FUTEX_WAKE_BITSET_PRIVATE, c_one, nullptr, nullptr, c_sig - 1); #endif }
void testcase(unsigned long long *iterations) { while (1) { unsigned int addr = 0; futex(&addr, FUTEX_WAIT_PRIVATE, 1, NULL, NULL, 0); (*iterations)++; } }
int do_unlock_contended_pi_futex(void) { if (futex(&pi_futex->counter, FUTEX_UNLOCK_PI, 1, NULL, NULL, 0) == 0) return 0; /* * There are still some lower priority waiters we failed to * wake for some reason. Documentation/pi-futex.txt fails * to mention what FUTEX_UNLOCK_PI returns! */ switch(errno) { case ERESTART: case EINTR: log("INFO", "retrying release_pi_futex since interrupted\n"); return 1; case EFAULT: /* We specified the wrong pi_futex address. */ log("FAIL", "wrong futex address or page fault/futex race in-kernel.\n"); break; case EINVAL: /* * The old value is wrong. We should never * get this since the kernel ignores the val * passed through sys_futex(). */ log("FAIL", "kernel got confused and lost the old futex value.\n"); break; case EPERM: /* * We are unable to release the futex. * We may not be holding it like we think * we do. */ log_error("This process seems to lack permission to release a futex it expects to be holding. Maybe it's not being held?\n"); break; case EAGAIN: /* * Task holding the futex is exiting. Odd, * that's us! */ log("FAIL", "kernel insists we're exiting but we're really not!\n"); break; case ENOMEM: log_error(""); break; case ESRCH: /* * Task that held the futex is no more?! But * that's us! */ log("FAIL", "The kernel can't seem to find this process! I sense impending doom!\n"); break; } return -1; }
void shared_mutex::imp_lock_unlock() { u32 _max = 1; for (int i = 0; i < 30; i++) { const u32 val = m_value; if (val % c_one == 0 && (val / c_one < _max || val >= c_sig)) { // Return if have cought a state where: // 1) Mutex is free // 2) Total number of waiters decreased since last check // 3) Signal bit is set (if used on the platform) return; } _max = val / c_one; busy_wait(1500); } #ifndef _WIN32 while (false) { const u32 val = m_value; if (val % c_one == 0 && (val / c_one < _max || val >= c_sig)) { return; } if (val <= c_one) { // Can't expect a signal break; } _max = val / c_one; // Monitor all bits except c_sig futex(reinterpret_cast<int*>(&m_value.raw()), FUTEX_WAIT_BITSET_PRIVATE, val, nullptr, nullptr, c_sig - 1); } #endif // Lock and unlock if (!m_value.fetch_add(c_one)) { unlock(); return; } imp_wait(); unlock(); }
bool cond_variable::imp_wait(u32 _old, u64 _timeout) noexcept { verify(HERE), _old != -1; // Very unlikely: it requires 2^32 distinct threads to wait simultaneously #ifdef _WIN32 LARGE_INTEGER timeout; timeout.QuadPart = _timeout * -10; if (HRESULT rc = NtWaitForKeyedEvent(nullptr, &m_value, false, _timeout == -1 ? nullptr : &timeout)) { verify(HERE), rc == WAIT_TIMEOUT; // Retire if (!m_value.fetch_op([](u32& value) { if (value) value--; })) { NtWaitForKeyedEvent(nullptr, &m_value, false, nullptr); return true; } return false; } return true; #elif __linux__ timespec timeout; timeout.tv_sec = _timeout / 1000000; timeout.tv_nsec = (_timeout % 1000000) * 1000; for (u32 value = _old + 1;; value = m_value) { const int err = futex((int*)&m_value.raw(), FUTEX_WAIT_PRIVATE, value, _timeout == -1 ? nullptr : &timeout, nullptr, 0) == 0 ? 0 : errno; // Normal or timeout wakeup if (!err || (_timeout != -1 && err == ETIMEDOUT)) { // Cleanup (remove waiter) verify(HERE), m_value--; return !err; } // Not a wakeup verify(HERE), err == EAGAIN; } #else // TODO std::this_thread::sleep_for(std::chrono::microseconds(50)); verify(HERE), m_value--; return true; #endif }
static inline void futex_wait(QemuEvent *ev, unsigned val) { while (futex(ev, FUTEX_WAIT, (int) val, NULL, NULL, 0)) { switch (errno) { case EWOULDBLOCK: return; case EINTR: break; /* get out of switch and retry */ default: abort(); } } }
int bbgl_mutex_unlock(bbgl_mutex_t *mutex) { if (*mutex == 2) *mutex = 0; else if (xchg(mutex, 0) == 1) return 0; for (int i = 0; i < 200; i++) { if (*mutex) if (cmpxchg(mutex, 1, 2)) return 0; __asm__ __volatile__("pause" ::: "memory"); } futex(mutex, FUTEX_WAKE, 1, NULL, NULL, 0); return 0; }
void cond_variable::imp_wake(u32 _count) noexcept { #ifdef _WIN32 // Try to subtract required amount of waiters const u32 count = m_value.atomic_op([=](u32& value) { if (value > _count) { value -= _count; return _count; } return std::exchange(value, 0); }); for (u32 i = count; i > 0; i--) { NtReleaseKeyedEvent(nullptr, &m_value, false, nullptr); } #elif __linux__ for (u32 i = _count; i > 0; sched_yield()) { const u32 value = m_value; // Constrain remaining amount with imaginary waiter count if (i > value) { i = value; } if (!value || i == 0) { // Nothing to do return; } if (const int res = futex((int*)&m_value.raw(), FUTEX_WAKE_PRIVATE, i > INT_MAX ? INT_MAX : i, nullptr, nullptr, 0)) { verify(HERE), res >= 0 && res <= i; i -= res; } if (!m_value || i == 0) { // Escape return; } } #endif }
/* futex post & wait */ void wait_futex_sem(struct lockinfo *l) { int ret; l->data = 1; worklist_add(l); while(l->data == 1) { ret = futex(&l->data, FUTEX_WAIT, 1, NULL, NULL, 0); /* if (ret && ret != EWOULDBLOCK) { perror("futex wait"); exit(1); }*/ } }
// If any procs are sleeping on addr, wake up at most cnt. static void futexwakeup(uint32 *addr, uint32 cnt) { int64 ret; ret = runtime·futex(addr, FUTEX_WAKE, cnt, nil, nil, 0); if(ret >= 0) return; // I don't know that futex wakeup can return // EAGAIN or EINTR, but if it does, it would be // safe to loop and call futex again. runtime·printf("futexwakeup addr=%p returned %D\n", addr, ret); *(int32*)0x1006 = 0x1006; }
int bbgl_mutex_lock(bbgl_mutex_t *mutex) { int c; for (int i = 0; i < 100; i++) { c = cmpxchg(mutex, 0, 1); if (c == 0) return 0; __asm__ __volatile__("pause" ::: "memory"); } if (c == 1) c = xchg(mutex, 2); while (c) { futex(mutex, FUTEX_WAIT, 2, NULL, NULL, 0); c = xchg(mutex, 2); } return 0; }
int main(int argc, char *argv[]) { const size_t stack_size = 1 << 20; void* stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); int pid; sigset_t set; sys_gettid(); /* NB: no syscalls in between the sys_gettid() above and this * clone(). */ breakpoint(); /* Warning: strace gets the parameter order wrong and will print child_tidptr as 0 here. */ pid = clone(child, stack + stack_size, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SIGHAND | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID, NULL, &child_tid, NULL, &child_tid); atomic_printf("clone()d pid: %d\n", pid); test_assert(pid > 0); futex(&child_tid, FUTEX_WAIT, pid, NULL, NULL, 0); test_assert(child_tid_copy == pid); /* clone() should have cleared child_tid now */ test_assert(child_tid == 0); sys_gettid(); sigfillset(&set); test_assert(0 == sigprocmask(SIG_BLOCK, &set, NULL)); /* NB: no syscalls in between the sys_gettid() above and this * clone(). */ breakpoint(); pid = clone(child, stack + stack_size, CLONE_SIGHAND /*must also have CLONE_VM*/, NULL, NULL, NULL); atomic_printf("clone(CLONE_SIGHAND)'d pid: %d\n", pid); test_assert(-1 == pid); atomic_puts("EXIT-SUCCESS"); return 0; }
int futex_post(fsem_t* p) { int err = 0; if (__sync_fetch_and_add(&p->val_, 1) >= INT_MAX) { err = EOVERFLOW; } else { __sync_synchronize(); // 这个barrier完全是从glibc中抄过来的,其实可以不要。 if (p->nwaiters_ > 0) { futex(&p->val_, FUTEX_WAKE, 1, NULL, NULL, 0); } } return err; }
int kid(void *trash) { atomic_inc(&dumb_barrier[0]); /* 1 */ again: if (futex(&test_futex->counter, FUTEX_WAIT, -1, NULL, NULL, 0) != 0) { switch(errno) { case ETIMEDOUT: log_error("FUTEX_WAIT ETIMEDOUT"); break; case ERESTART: log("INFO", "RESTARTING FUTEX_WAIT (I think I was FROZEN)"); goto again; case EAGAIN: /* EWOULDBLOCK */ if (atomic_read(test_futex) == 1) { log("INFO", "kid: I was interrupted.\n"); log("INFO", "kid: and now test_futex==1, so I'm done\n"); break; } log("INFO", "FUTEX_WAIT EAGAIN"); goto again; break; case EINTR: log("INFO", "FUTEX_WAIT EINTR"); goto again; break; case EACCES: log("FAIL", "FUTEX_WAIT EACCES - no read access to futex memory\n"); break; case EFAULT: log("FAIL", "FUTEX_WAIT EFAULT - bad timeout timespec address or futex address\n"); break; case EINVAL: log("FAIL", "FUTEX_WAIT EINVAL - undefined futex operation\n"); break; case ENOSYS: log("FAIL", "FUTEX_WAIT ENOSYS - undefined futex operation\n"); break; default: log_error("FUTEX_WAIT unexpected error (missing from man page)"); break; } } atomic_inc(&dumb_barrier[1]); /* 2 */ return 0; }
// Atomically, // if(*addr == val) sleep // Might be woken up spuriously; that's allowed. static void futexsleep(uint32 *addr, uint32 val) { int32 ret; ret = futex(addr, FUTEX_WAIT, val, &longtime, nil, 0); if(ret >= 0 || ret == -EAGAIN || ret == -EINTR) return; prints("futexsleep addr="); ·printpointer(addr); prints(" val="); ·printint(val); prints(" returned "); ·printint(ret); prints("\n"); *(int32*)0x1005 = 0x1005; }
int wake_waitq(atomic_t *wq, int retries) { unsigned int woken = 0; int ret; atomic_set(wq, 1); do { ret = futex(&wq->counter, FUTEX_WAKE, N - 1 - woken, NULL, NULL, 0); retries--; if (ret > 0) woken += ret; } while (retries && woken < N - 1); if (woken < N - 1) { log("WARN", "Could not wake %d children. Woke %d instead. waitq: %d\n", N - 1, woken, atomic_read(waitq)); log_error(" "); } return -1; }
// If any procs are sleeping on addr, wake up at least one. static void futexwakeup(uint32 *addr) { int64 ret; ret = futex(addr, FUTEX_WAKE, 1, nil, nil, 0); if(ret >= 0) return; // I don't know that futex wakeup can return // EAGAIN or EINTR, but if it does, it would be // safe to loop and call futex again. prints("futexwakeup addr="); ·printpointer(addr); prints(" returned "); ·printint(ret); prints("\n"); *(int32*)0x1006 = 0x1006; }
void *handler(void *arg) { int id = pthread_self()->id; int var = 0; struct timespec timeout = { .tv_sec = id, .tv_nsec = 0 }; printf("Begin thread: %d\n", id); futex(&var, FUTEX_WAIT, 0, &timeout, NULL, 0); printf("End thread: %d\n", id); } int main(int argc, char **argv) { for (int i=0; i<NUM_THREADS; i++) { pthread_create(&thandlers[i], NULL, &handler, NULL); } for (int i=0; i<NUM_THREADS; i++) { pthread_join(thandlers[i], NULL); } return 0; }
int futex_wake_some(struct sem_wakeup_info *wi, int num_semids, int num) { int i; int ret; struct lockinfo *l; int found = 0; for (i = 0; i < num; i++) { l = worklist_rm(); if (!l) break; if (l->data != 1) fprintf(stderr, "warning, lockinfo data was %d\n", l->data); l->data = 0; ret = futex(&l->data, FUTEX_WAKE, 1, NULL, NULL, 0); if (ret < 0) { perror("futex wake"); exit(1); } found++; } return found; }
int os_futex_wake(int *address, int count) { return futex(address, FUTEX_WAKE, count, nullptr, nullptr, 0) ? errno : 0; }
int os_futex_wait(int *address, int val) { return futex(address, FUTEX_WAIT, val, nullptr, nullptr, 0) ? errno : 0; }
int main(int argc, char **argv) { pid_t kids[N]; int i, num_killed = 0, excode = EXIT_FAILURE; char *freezerdir; if (argc < 2) exit(1); freezerdir = argv[1]; if (!move_to_cgroup("freezer", freezerdir, getpid())) { printf("Failed to move myself to cgroup\n"); exit(1); } /* FIXME eventually stdio streams should be harmless */ close(0); logfp = fopen(LOG_FILE, "w"); if (!logfp) { perror("could not open logfile"); exit(1); } dup2(fileno(logfp), 1); /* redirect stdout and stderr to the log file */ dup2(fileno(logfp), 2); test_futex = alloc_futex_mem(sizeof(*test_futex)); if (!test_futex) { log_error("alloc_futex_mem"); exit(3); } atomic_set(test_futex, -1); signal(SIGINT, sig_dump); for (i = 0; i < N; i++) { char *new_stack = malloc(SIGSTKSZ*8); if (!new_stack) { i--; break; } kids[i] = clone(kid, new_stack + SIGSTKSZ*8, clone_flags, NULL); if (kids[i] < 0) { i--; break; } } if (i < N) { log_error("N x FUTEX_WAIT"); log("INFO", "killing %d child tasks.\n", i); for (; --i > -1;) kill(kids[i], SIGTERM); _exit(4); } /* parent */ log("INFO", "Waiting for children to sleep on futex\n"); while (atomic_read(&dumb_barrier[0]) != N) /* 1 */ sleep(1); dump("After 1, before 2:"); sleep(1); log("INFO", "signaling ready for checkpointing\n"); set_checkpoint_ready(); while (!test_checkpoint_done()) { sleep(1); } log("INFO", "Parent woken\n"); atomic_set(test_futex, 1); dump("After 1, cleared test_futex, before 2:"); i = futex(&test_futex->counter, FUTEX_WAKE, N, NULL, NULL, 0); if (i == -1) { fprintf(logfp, "futex_wake N=%d returned %d\n", N, i); log_error("FUTEX_WAKE"); sleep(1); /* wait for all woken tasks to exit quietly */ /* kill the rest */ for (i = 0; i < N; i++) { if (kill(kids[i], SIGKILL) == 0) num_killed++; } if (num_killed) log("INFO", "killed %d remaining child tasks.\n", num_killed); } else excode = EXIT_SUCCESS; dump("After 2:"); do_wait(N); dump("After 3:"); fclose(logfp); exit(excode); }
void ThreadEntry::Wake() { futex_++; futex(&futex_, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); }