2011-10-08 Paul Thomas <pault@gcc.gnu.org>
[official-gcc.git] / libgo / runtime / go-rec-small.c
blobd94763296941b631aa9e214f64cbc7575f7d283e
1 /* go-rec-small.c -- receive something smaller than 64 bits on a channel.
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 "go-assert.h"
10 #include "go-panic.h"
11 #include "channel.h"
13 /* This mutex controls access to the selected field of struct
14 __go_channel_select. While this mutex is held, no other mutexes
15 may be acquired. */
17 pthread_mutex_t __go_select_data_mutex = PTHREAD_MUTEX_INITIALIZER;
19 /* Try to synchronize with a select waiting on a sychronized channel.
20 This is used by a send or receive. The channel is locked. This
21 returns true if it was able to synch. */
23 _Bool
24 __go_synch_with_select (struct __go_channel *channel, _Bool is_send)
26 struct __go_channel_select *p;
27 int i;
29 __go_assert (channel->num_entries == 0);
31 i = pthread_mutex_lock (&__go_select_data_mutex);
32 __go_assert (i == 0);
34 for (p = (is_send
35 ? channel->select_receive_queue
36 : channel->select_send_queue);
37 p != NULL;
38 p = p->next)
40 if (*p->selected == NULL)
42 *p->selected = channel;
43 *p->is_read = !is_send;
44 if (is_send)
45 channel->selected_for_receive = 1;
46 else
47 channel->selected_for_send = 1;
48 break;
52 i = pthread_mutex_unlock (&__go_select_data_mutex);
53 __go_assert (i == 0);
55 /* The caller is responsible for signalling the select condition
56 variable so that the other select knows that something has
57 changed. We can't signal it here because we can't acquire the
58 select mutex while we hold a channel lock. */
60 return p != NULL;
63 /* If we synch with a select, then we need to signal the select that
64 something has changed. This requires grabbing the select mutex,
65 which can only be done when the channel is unlocked. This routine
66 does the signalling. It is called with the channel locked. It
67 unlocks the channel, broadcasts the signal and relocks the
68 channel. */
70 void
71 __go_broadcast_to_select (struct __go_channel *channel)
73 pthread_mutex_t *select_mutex;
74 pthread_cond_t *select_cond;
75 int i;
77 select_mutex = channel->select_mutex;
78 select_cond = channel->select_cond;
80 i = pthread_mutex_unlock (&channel->lock);
81 __go_assert (i == 0);
83 __go_assert (select_mutex != NULL && select_cond != NULL);
85 i = pthread_mutex_lock (select_mutex);
86 __go_assert (i == 0);
88 i = pthread_cond_broadcast (select_cond);
89 __go_assert (i == 0);
91 i = pthread_mutex_unlock (select_mutex);
92 __go_assert (i == 0);
94 i = pthread_mutex_lock (&channel->lock);
95 __go_assert (i == 0);
98 /* Prepare to receive something on a channel. Return true if the
99 channel is acquired (which implies that there is data available),
100 false if it is closed. */
102 _Bool
103 __go_receive_acquire (struct __go_channel *channel, _Bool for_select)
105 int i;
106 _Bool my_wait_lock;
107 _Bool synched_with_select;
109 my_wait_lock = 0;
110 synched_with_select = 0;
112 i = pthread_mutex_lock (&channel->lock);
113 __go_assert (i == 0);
115 while (1)
117 _Bool need_broadcast;
119 need_broadcast = 0;
121 /* Check whether the channel is closed. */
122 if (channel->is_closed
123 && (channel->num_entries == 0
124 ? channel->next_store == 0
125 : channel->next_fetch == channel->next_store))
127 channel->selected_for_receive = 0;
128 __go_unlock_and_notify_selects (channel);
129 return 0;
132 /* If somebody else has the channel locked for receiving, we
133 have to wait. If FOR_SELECT is true, then we are the one
134 with the lock. */
135 if (!channel->selected_for_receive || for_select)
137 if (channel->num_entries == 0)
139 /* If somebody else is waiting to receive, we have to
140 wait. */
141 if (!channel->waiting_to_receive || my_wait_lock)
143 _Bool was_marked;
145 /* Lock the channel so that we get to receive
146 next. */
147 was_marked = channel->waiting_to_receive;
148 channel->waiting_to_receive = 1;
149 my_wait_lock = 1;
151 /* See if there is a value to receive. */
152 if (channel->next_store > 0)
153 return 1;
155 /* If we haven't already done so, try to synch with
156 a select waiting to send on this channel. If we
157 have already synched with a select, we are just
158 looping until the select eventually causes
159 something to be sent. */
160 if (!synched_with_select && !for_select)
162 if (__go_synch_with_select (channel, 0))
164 synched_with_select = 1;
165 need_broadcast = 1;
169 /* If we marked the channel as waiting, we need to
170 signal, because something changed. It needs to
171 be a broadcast since there might be other
172 receivers waiting. */
173 if (!was_marked)
175 i = pthread_cond_broadcast (&channel->cond);
176 __go_assert (i == 0);
180 else
182 /* If there is a value on the channel, we are OK. */
183 if (channel->next_fetch != channel->next_store)
184 return 1;
188 /* If we just synched with a select, then we need to signal the
189 select condition variable. We can only do that if we unlock
190 the channel. So we need to unlock, signal, lock, and go
191 around the loop again without waiting. */
192 if (need_broadcast)
194 __go_broadcast_to_select (channel);
195 continue;
198 /* Wait for something to change, then loop around and try
199 again. */
201 i = pthread_cond_wait (&channel->cond, &channel->lock);
202 __go_assert (i == 0);
206 /* Finished receiving something on a channel. */
208 void
209 __go_receive_release (struct __go_channel *channel)
211 int i;
213 if (channel->num_entries != 0)
214 channel->next_fetch = (channel->next_fetch + 1) % channel->num_entries;
215 else
217 /* For a synchronous receiver, we tell the sender that we picked
218 up the value by setting the next_store field back to 0.
219 Using the mutexes should implement a memory barrier. */
220 __go_assert (channel->next_store == 1);
221 channel->next_store = 0;
223 channel->waiting_to_receive = 0;
226 channel->selected_for_receive = 0;
228 /* This is a broadcast to make sure that a synchronous sender sees
229 it. */
230 i = pthread_cond_broadcast (&channel->cond);
231 __go_assert (i == 0);
233 __go_unlock_and_notify_selects (channel);
236 /* Unlock a channel and notify any waiting selects that something
237 happened. */
239 void
240 __go_unlock_and_notify_selects (struct __go_channel *channel)
242 pthread_mutex_t* select_mutex;
243 pthread_cond_t* select_cond;
244 int i;
246 select_mutex = channel->select_mutex;
247 select_cond = channel->select_cond;
249 i = pthread_mutex_unlock (&channel->lock);
250 __go_assert (i == 0);
252 if (select_mutex != NULL)
254 i = pthread_mutex_lock (select_mutex);
255 __go_assert (i == 0);
256 i = pthread_cond_broadcast (select_cond);
257 __go_assert (i == 0);
258 i = pthread_mutex_unlock (select_mutex);
259 __go_assert (i == 0);
263 /* Receive something 64 bits or smaller on a channel. */
265 uint64_t
266 __go_receive_small_closed (struct __go_channel *channel, _Bool for_select,
267 _Bool *received)
269 uintptr_t element_size;
270 uint64_t ret;
272 if (channel == NULL)
274 /* Block forever. */
275 __go_select (0, 0, NULL, NULL);
278 element_size = channel->element_type->__size;
279 __go_assert (element_size <= sizeof (uint64_t));
281 if (!__go_receive_acquire (channel, for_select))
283 if (received != NULL)
284 *received = 0;
285 return 0;
288 ret = channel->data[channel->next_fetch];
290 __go_receive_release (channel);
292 if (received != NULL)
293 *received = 1;
295 return ret;
298 /* Called by the compiler. */
300 uint64_t
301 __go_receive_small (struct __go_channel *channel, _Bool for_select)
303 return __go_receive_small_closed (channel, for_select, NULL);