diff options
author | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
---|---|---|
committer | upstream source tree <ports@midipix.org> | 2015-03-15 20:14:05 -0400 |
commit | 554fd8c5195424bdbcabf5de30fdc183aba391bd (patch) | |
tree | 976dc5ab7fddf506dadce60ae936f43f58787092 /libgo/runtime/go-semacquire.c | |
download | cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.bz2 cbb-gcc-4.6.4-15d2061ac0796199866debe9ac87130894b0cdd3.tar.xz |
obtained gcc-4.6.4.tar.bz2 from upstream website;upstream
verified gcc-4.6.4.tar.bz2.sig;
imported gcc-4.6.4 source tree from verified upstream tarball.
downloading a git-generated archive based on the 'upstream' tag
should provide you with a source tree that is binary identical
to the one extracted from the above tarball.
if you have obtained the source via the command 'git clone',
however, do note that line-endings of files in your working
directory might differ from line-endings of the respective
files in the upstream repository.
Diffstat (limited to 'libgo/runtime/go-semacquire.c')
-rw-r--r-- | libgo/runtime/go-semacquire.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/libgo/runtime/go-semacquire.c b/libgo/runtime/go-semacquire.c new file mode 100644 index 000000000..24c6a7388 --- /dev/null +++ b/libgo/runtime/go-semacquire.c @@ -0,0 +1,151 @@ +/* go-semacquire.c -- implement runtime.Semacquire and runtime.Semrelease. + + 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. */ + +#include <stdint.h> + +#include <pthread.h> + +#include "go-assert.h" +#include "runtime.h" + +/* We use a single global lock and condition variable. This is + painful, since it will cause unnecessary contention, but is hard to + avoid in a portable manner. On Linux we can use futexes, but they + are unfortunately not exposed by libc and are thus also hard to use + portably. */ + +static pthread_mutex_t sem_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t sem_cond = PTHREAD_COND_INITIALIZER; + +/* If the value in *ADDR is positive, and we are able to atomically + decrement it, return true. Otherwise do nothing and return + false. */ + +static _Bool +acquire (uint32 *addr) +{ + while (1) + { + uint32 val; + + val = *addr; + if (val == 0) + return 0; + if (__sync_bool_compare_and_swap (addr, val, val - 1)) + return 1; + } +} + +/* Implement runtime.Semacquire. ADDR points to a semaphore count. + We have acquired the semaphore when we have decremented the count + and it remains nonnegative. */ + +void +semacquire (uint32 *addr) +{ + while (1) + { + int i; + + /* If the current count is positive, and we are able to atomically + decrement it, then we have acquired the semaphore. */ + if (acquire (addr)) + return; + + /* Lock the mutex. */ + i = pthread_mutex_lock (&sem_lock); + __go_assert (i == 0); + + /* Check the count again with the mutex locked. */ + if (acquire (addr)) + { + i = pthread_mutex_unlock (&sem_lock); + __go_assert (i == 0); + return; + } + + /* The count is zero. Even if a call to runtime.Semrelease + increments it to become positive, that call will try to + acquire the mutex and block, so we are sure to see the signal + of the condition variable. */ + i = pthread_cond_wait (&sem_cond, &sem_lock); + __go_assert (i == 0); + + /* Unlock the mutex and try again. */ + i = pthread_mutex_unlock (&sem_lock); + __go_assert (i == 0); + } +} + +/* Implement runtime.Semrelease. ADDR points to a semaphore count. We + must atomically increment the count. If the count becomes + positive, we signal the condition variable to wake up another + process. */ + +void +semrelease (uint32 *addr) +{ + int32_t val; + + val = __sync_fetch_and_add (addr, 1); + + /* VAL is the old value. It should never be negative. If it is + negative, that implies that Semacquire somehow decremented a zero + value, or that the count has overflowed. */ + __go_assert (val >= 0); + + /* If the old value was zero, then we have now released a count, and + we signal the condition variable. If the old value was positive, + then nobody can be waiting. We have to use + pthread_cond_broadcast, not pthread_cond_signal, because + otherwise there would be a race condition when the count is + incremented twice before any locker manages to decrement it. */ + if (val == 0) + { + int i; + + i = pthread_mutex_lock (&sem_lock); + __go_assert (i == 0); + + i = pthread_cond_broadcast (&sem_cond); + __go_assert (i == 0); + + i = pthread_mutex_unlock (&sem_lock); + __go_assert (i == 0); + } +} + + +#ifndef HAVE_SYNC_FETCH_AND_ADD_4 + +/* For targets which don't have the required sync support. Really + this should be provided by gcc itself. FIXME. */ + +static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER; + +uint32 +__sync_fetch_and_add_4(uint32*, uint32) + __attribute__((visibility("hidden"))); + +uint32 +__sync_fetch_and_add_4(uint32* ptr, uint32 add) +{ + int i; + uint32 ret; + + i = pthread_mutex_lock(&sync_lock); + __go_assert(i == 0); + + ret = *ptr; + *ptr += add; + + i = pthread_mutex_unlock(&sync_lock); + __go_assert(i == 0); + + return ret; +} + +#endif |