URL
https://opencores.org/ocsvn/openrisc/openrisc/trunk
Subversion Repositories openrisc
[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libgo/] [runtime/] [sema.goc] - Rev 747
Compare with Previous | Blame | View Log
// 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.pdfpackage runtime#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 251static union{SemaRoot;uint8 pad[CacheLineSize];} semtable[SEMTABLESZ];static SemaRoot*semroot(uint32 volatile *addr){return &semtable[((uintptr)addr >> 3) % SEMTABLESZ];}static voidsemqueue(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;elseroot->head = s;root->tail = s;}static voidsemdequeue(SemaRoot *root, Sema *s){if(s->next)s->next->prev = s->prev;elseroot->tail = s->prev;if(s->prev)s->prev->next = s->next;elseroot->head = s->next;s->prev = nil;s->next = nil;}static int32cansemacquire(uint32 volatile *addr){uint32 v;while((v = runtime_atomicload(addr)) > 0)if(runtime_cas(addr, v, v-1))return 1;return 0;}voidruntime_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;}}voidruntime_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 Semacquire(addr *uint32) {runtime_semacquire(addr);}func Semrelease(addr *uint32) {runtime_semrelease(addr);}
