From 82bcbebce43f0227f506d75a5b764b6847041bae Mon Sep 17 00:00:00 2001 From: Ben Cheng Date: Mon, 1 Oct 2012 10:30:31 -0700 Subject: Initial check-in of gcc 4.7.2. Change-Id: I4a2f5a921c21741a0e18bda986d77e5f1bef0365 --- gcc-4.7/libgo/runtime/sema.goc | 181 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 gcc-4.7/libgo/runtime/sema.goc (limited to 'gcc-4.7/libgo/runtime/sema.goc') diff --git a/gcc-4.7/libgo/runtime/sema.goc b/gcc-4.7/libgo/runtime/sema.goc new file mode 100644 index 000000000..ff9c4f2e1 --- /dev/null +++ b/gcc-4.7/libgo/runtime/sema.goc @@ -0,0 +1,181 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Semaphore implementation exposed to Go. +// Intended use is provide a sleep and wakeup +// primitive that can be used in the contended case +// of other synchronization primitives. +// Thus it targets the same goal as Linux's futex, +// but it has much simpler semantics. +// +// That is, don't think of these as semaphores. +// Think of them as a way to implement sleep and wakeup +// such that every sleep is paired with a single wakeup, +// even if, due to races, the wakeup happens before the sleep. +// +// See Mullender and Cox, ``Semaphores in Plan 9,'' +// http://swtch.com/semaphore.pdf + +package sync +#include "runtime.h" +#include "arch.h" + +typedef struct Sema Sema; +struct Sema +{ + uint32 volatile *addr; + G *g; + Sema *prev; + Sema *next; +}; + +typedef struct SemaRoot SemaRoot; +struct SemaRoot +{ + Lock; + Sema *head; + Sema *tail; + // Number of waiters. Read w/o the lock. + uint32 volatile nwait; +}; + +// Prime to not correlate with any user patterns. +#define SEMTABLESZ 251 + +static union +{ + SemaRoot; + uint8 pad[CacheLineSize]; +} semtable[SEMTABLESZ]; + +static SemaRoot* +semroot(uint32 volatile *addr) +{ + return &semtable[((uintptr)addr >> 3) % SEMTABLESZ]; +} + +static void +semqueue(SemaRoot *root, uint32 volatile *addr, Sema *s) +{ + s->g = runtime_g(); + s->addr = addr; + s->next = nil; + s->prev = root->tail; + if(root->tail) + root->tail->next = s; + else + root->head = s; + root->tail = s; +} + +static void +semdequeue(SemaRoot *root, Sema *s) +{ + if(s->next) + s->next->prev = s->prev; + else + root->tail = s->prev; + if(s->prev) + s->prev->next = s->next; + else + root->head = s->next; + s->prev = nil; + s->next = nil; +} + +static int32 +cansemacquire(uint32 volatile *addr) +{ + uint32 v; + + while((v = runtime_atomicload(addr)) > 0) + if(runtime_cas(addr, v, v-1)) + return 1; + return 0; +} + +void +runtime_semacquire(uint32 volatile *addr) +{ + G *g; + Sema s; + SemaRoot *root; + + // Easy case. + if(cansemacquire(addr)) + return; + + // Harder case: + // increment waiter count + // try cansemacquire one more time, return if succeeded + // enqueue itself as a waiter + // sleep + // (waiter descriptor is dequeued by signaler) + g = runtime_g(); + root = semroot(addr); + for(;;) { + + runtime_lock(root); + // Add ourselves to nwait to disable "easy case" in semrelease. + runtime_xadd(&root->nwait, 1); + // Check cansemacquire to avoid missed wakeup. + if(cansemacquire(addr)) { + runtime_xadd(&root->nwait, -1); + runtime_unlock(root); + return; + } + // Any semrelease after the cansemacquire knows we're waiting + // (we set nwait above), so go to sleep. + semqueue(root, addr, &s); + g->status = Gwaiting; + g->waitreason = "semacquire"; + runtime_unlock(root); + runtime_gosched(); + if(cansemacquire(addr)) + return; + } +} + +void +runtime_semrelease(uint32 volatile *addr) +{ + Sema *s; + SemaRoot *root; + + root = semroot(addr); + runtime_xadd(addr, 1); + + // Easy case: no waiters? + // This check must happen after the xadd, to avoid a missed wakeup + // (see loop in semacquire). + if(runtime_atomicload(&root->nwait) == 0) + return; + + // Harder case: search for a waiter and wake it. + runtime_lock(root); + if(runtime_atomicload(&root->nwait) == 0) { + // The count is already consumed by another goroutine, + // so no need to wake up another goroutine. + runtime_unlock(root); + return; + } + for(s = root->head; s; s = s->next) { + if(s->addr == addr) { + runtime_xadd(&root->nwait, -1); + semdequeue(root, s); + break; + } + } + runtime_unlock(root); + if(s) + runtime_ready(s->g); +} + +func runtime_Semacquire(addr *uint32) { + runtime_semacquire(addr); +} + +func runtime_Semrelease(addr *uint32) { + runtime_semrelease(addr); +} -- cgit v1.2.3