OSDN Git Service

Update Go library to last weekly.
[pf3gnuchains/gcc-fork.git] / libgo / runtime / go-semacquire.c
1 /* go-semacquire.c -- implement runtime.Semacquire and runtime.Semrelease.
2
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.  */
6
7 #include <stdint.h>
8
9 #include <pthread.h>
10
11 #include "go-assert.h"
12 #include "runtime.h"
13
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 GNU/Linux we can use futexes, but
17    they are unfortunately not exposed by libc and are thus also hard
18    to use portably.  */
19
20 static pthread_mutex_t sem_lock = PTHREAD_MUTEX_INITIALIZER;
21 static pthread_cond_t sem_cond = PTHREAD_COND_INITIALIZER;
22
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.  */
26
27 static _Bool
28 acquire (uint32 *addr)
29 {
30   while (1)
31     {
32       uint32 val;
33
34       val = *addr;
35       if (val == 0)
36         return 0;
37       if (__sync_bool_compare_and_swap (addr, val, val - 1))
38         return 1;
39     }
40 }
41
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.  */
45
46 void
47 runtime_semacquire (uint32 *addr)
48 {
49   while (1)
50     {
51       int i;
52
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;
57
58       /* Lock the mutex.  */
59       i = pthread_mutex_lock (&sem_lock);
60       __go_assert (i == 0);
61
62       /* Check the count again with the mutex locked.  */
63       if (acquire (addr))
64         {
65           i = pthread_mutex_unlock (&sem_lock);
66           __go_assert (i == 0);
67           return;
68         }
69
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);
76
77       /* Unlock the mutex and try again.  */
78       i = pthread_mutex_unlock (&sem_lock);
79       __go_assert (i == 0);
80     }
81 }
82
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.  */
87
88 void
89 runtime_semrelease (uint32 *addr)
90 {
91   int32_t val;
92
93   val = __sync_fetch_and_add (addr, 1);
94
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);
99
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)
107     {
108       int i;
109
110       i = pthread_mutex_lock (&sem_lock);
111       __go_assert (i == 0);
112
113       i = pthread_cond_broadcast (&sem_cond);
114       __go_assert (i == 0);
115
116       i = pthread_mutex_unlock (&sem_lock);
117       __go_assert (i == 0);
118     }
119 }
120
121
122 #ifndef HAVE_SYNC_FETCH_AND_ADD_4
123
124 /* For targets which don't have the required sync support.  Really
125    this should be provided by gcc itself.  FIXME.  */
126
127 static pthread_mutex_t sync_lock = PTHREAD_MUTEX_INITIALIZER;
128
129 uint32
130 __sync_fetch_and_add_4(uint32*, uint32)
131   __attribute__((visibility("hidden")));
132
133 uint32
134 __sync_fetch_and_add_4(uint32* ptr, uint32 add)
135 {
136   int i;
137   uint32 ret;
138
139   i = pthread_mutex_lock(&sync_lock);
140   __go_assert(i == 0);
141
142   ret = *ptr;
143   *ptr += add;
144
145   i = pthread_mutex_unlock(&sync_lock);
146   __go_assert(i == 0);
147
148   return ret;
149 }
150
151 #endif