1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
/* 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);
}
}
|