/* * Acquire ticket lock. Increment the tail of the queue and use the original * value as the ticket value. Wait until the head of the queue equals the * ticket value. The futex used to wait depends on the ticket value in order * to avoid that all threads get woken up every time a ticket lock is * released. That last effect is sometimes called the "thundering herd" * effect. * * See also Nick Piggin, x86: FIFO ticket spinlocks, Linux kernel mailing list * (http://lkml.org/lkml/2007/11/1/125) for more info. */ static void acquire_sched_lock(struct sched_lock *p, ThreadId tid, SchedLockKind slk) { unsigned ticket, futex_value; volatile unsigned *futex; SysRes sres; ticket = __sync_fetch_and_add(&p->tail, 1); futex = &p->futex[ticket & TL_FUTEX_MASK]; if (s_debug) VG_(printf)("[%d/%d] acquire: ticket %d\n", VG_(getpid)(), VG_(gettid)(), ticket); for (;;) { futex_value = *futex; __sync_synchronize(); if (ticket == p->head) break; if (s_debug) VG_(printf)("[%d/%d] acquire: ticket %d - waiting until" " futex[%ld] != %d\n", VG_(getpid)(), VG_(gettid)(), ticket, (long)(futex - p->futex), futex_value); sres = VG_(do_syscall3)(__NR_futex, (UWord)futex, VKI_FUTEX_WAIT | VKI_FUTEX_PRIVATE_FLAG, futex_value); if (sr_isError(sres) && sres._val != VKI_EAGAIN) { VG_(printf)("futex_wait() returned error code %ld\n", sres._val); vg_assert(False); } } __sync_synchronize(); INNER_REQUEST(ANNOTATE_RWLOCK_ACQUIRED(p, /*is_w*/1)); vg_assert(p->owner == 0); p->owner = VG_(gettid)(); }
/* get a token */ void ML_(sema_down)( vg_sema_t *sema, Bool as_LL ) { HChar buf[2]; Int ret; Int lwpid = VG_(gettid)(); vg_assert(sema->owner_lwpid != lwpid); /* can't have it already */ vg_assert(sema->pipe[0] != sema->pipe[1]); again: buf[0] = buf[1] = 0; ret = VG_(read)(sema->pipe[0], buf, 1); INNER_REQUEST(ANNOTATE_RWLOCK_ACQUIRED(sema, /*is_w*/1)); if (ret != 1) VG_(debugLog)(0, "scheduler", "VG_(sema_down): read returned %d\n", ret); if (ret == -VKI_EINTR) goto again; vg_assert(ret == 1); /* should get exactly 1 token */ vg_assert(buf[0] >= 'A' && buf[0] <= 'Z'); vg_assert(buf[1] == 0); if (sema_char == 'Z') sema_char = 'A'; else sema_char++; sema->owner_lwpid = lwpid; sema->held_as_LL = as_LL; }
static void rwlock_wrlock(rwlock_t* p) { while (1) { while (__sync_val_compare_and_swap(&p->locked, 0, 1) == 1) ; if (p->reader_count == 0) break; pthread_yield(); __sync_fetch_and_sub(&p->locked, 1); } p->writer_count++; assert(p->reader_count >= 0); assert(p->writer_count >= 0); assert(p->reader_count == 0 || p->writer_count == 0); __sync_fetch_and_sub(&p->locked, 1); ANNOTATE_RWLOCK_ACQUIRED(p, 1); }