Fix PR47707
[official-gcc.git] / libgo / runtime / go-semacquire.c
blob24c6a7388f6a2ed792e74773242299dd99e57003
1 /* go-semacquire.c -- implement runtime.Semacquire and runtime.Semrelease.
3 Copyright 2009 The Go Authors. All rights reserved.
4 Use of this source code is governed by a BSD-style
5 license that can be found in the LICENSE file. */
7 #include <stdint.h>
9 #include <pthread.h>
11 #include "go-assert.h"
12 #include "runtime.h"
14 /* We use a single global lock and condition variable. This is
15 painful, since it will cause unnecessary contention, but is hard to
16 avoid in a portable manner. On Linux we can use futexes, but they
17 are unfortunately not exposed by libc and are thus also hard to use
18 portably. */
20 static pthread_mutex_t sem_lock = PTHREAD_MUTEX_INITIALIZER;
21 static pthread_cond_t sem_cond = PTHREAD_COND_INITIALIZER;
23 /* If the value in *ADDR is positive, and we are able to atomically
24 decrement it, return true. Otherwise do nothing and return
25 false. */
27 static _Bool
28 acquire (uint32 *addr)
30 while (1)
32 uint32 val;
34 val = *addr;
35 if (val == 0)
36 return 0;
37 if (__sync_bool_compare_and_swap (addr, val, val - 1))
38 return 1;
42 /* Implement runtime.Semacquire. ADDR points to a semaphore count.
43 We have acquired the semaphore when we have decremented the count
44 and it remains nonnegative. */
46 void
47 semacquire (uint32 *addr)
49 while (1)
51 int i;
53 /* If the current count is positive, and we are able to atomically
54 decrement it, then we have acquired the semaphore. */
55 if (acquire (addr))
56 return;
58 /* Lock the mutex. */
59 i = pthread_mutex_lock (&sem_lock);
60 __go_assert (i == 0);
62 /* Check the count again with the mutex locked. */
63 if (acquire (addr))
65 i = pthread_mutex_unlock (&sem_lock);
66 __go_assert (i == 0);
67 return;
70 /* The count is zero. Even if a call to runtime.Semrelease
71 increments it to become positive, that call will try to
72 acquire the mutex and block, so we are sure to see the signal
73 of the condition variable. */
74 i = pthread_cond_wait (&sem_cond, &sem_lock);
75 __go_assert (i == 0);
77 /* Unlock the mutex and try again. */
78 i = pthread_mutex_unlock (&sem_lock);
79 __go_assert (i == 0);
83 /* Implement runtime.Semrelease. ADDR points to a semaphore count. We
84 must atomically increment the count. If the count becomes
85 positive, we signal the condition variable to wake up another
86 process. */
88 void
89 semrelease (uint32 *addr)
91 int32_t val;
93 val = __sync_fetch_and_add (addr, 1);
95 /* VAL is the old value. It should never be negative. If it is
96 negative, that implies that Semacquire somehow decremented a zero
97 value, or that the count has overflowed. */
98 __go_assert (val >= 0);
100 /* If the old value was zero, then we have now released a count, and
101 we signal the condition variable. If the old value was positive,
102 then nobody can be waiting. We have to use
103 pthread_cond_broadcast, not pthread_cond_signal, because
104 otherwise there would be a race condition when the count is
105 incremented twice before any locker manages to decrement it. */
106 if (val == 0)
108 int i;
110 i = pthread_mutex_lock (&sem_lock);
111 __go_assert (i == 0);
113 i = pthread_cond_broadcast (&sem_cond);
114 __go_assert (i == 0);
116 i = pthread_mutex_unlock (&sem_lock);
117 __go_assert (i == 0);
122 #ifndef HAVE_SYNC_FETCH_AND_ADD_4
124 /* For targets which don't have the required sync support. Really
125 this should be provided by gcc itself. FIXME. */
127 static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER;
129 uint32
130 __sync_fetch_and_add_4(uint32*, uint32)
131 __attribute__((visibility("hidden")));
133 uint32
134 __sync_fetch_and_add_4(uint32* ptr, uint32 add)
136 int i;
137 uint32 ret;
139 i = pthread_mutex_lock(&sync_lock);
140 __go_assert(i == 0);
142 ret = *ptr;
143 *ptr += add;
145 i = pthread_mutex_unlock(&sync_lock);
146 __go_assert(i == 0);
148 return ret;
151 #endif