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. */
13 /* This mutex controls access to the selected field of struct
14 __go_channel_select. While this mutex is held, no other mutexes
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. */
24 __go_synch_with_select (struct __go_channel
*channel
, _Bool is_send
)
26 struct __go_channel_select
*p
;
29 __go_assert (channel
->num_entries
== 0);
31 i
= pthread_mutex_lock (&__go_select_data_mutex
);
35 ? channel
->select_receive_queue
36 : channel
->select_send_queue
);
40 if (*p
->selected
== NULL
)
42 *p
->selected
= channel
;
43 *p
->is_read
= !is_send
;
45 channel
->selected_for_receive
= 1;
47 channel
->selected_for_send
= 1;
52 i
= pthread_mutex_unlock (&__go_select_data_mutex
);
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. */
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
71 __go_broadcast_to_select (struct __go_channel
*channel
)
73 pthread_mutex_t
*select_mutex
;
74 pthread_cond_t
*select_cond
;
77 select_mutex
= channel
->select_mutex
;
78 select_cond
= channel
->select_cond
;
80 i
= pthread_mutex_unlock (&channel
->lock
);
83 __go_assert (select_mutex
!= NULL
&& select_cond
!= NULL
);
85 i
= pthread_mutex_lock (select_mutex
);
88 i
= pthread_cond_broadcast (select_cond
);
91 i
= pthread_mutex_unlock (select_mutex
);
94 i
= pthread_mutex_lock (&channel
->lock
);
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. */
103 __go_receive_acquire (struct __go_channel
*channel
, _Bool for_select
)
107 _Bool synched_with_select
;
110 synched_with_select
= 0;
112 i
= pthread_mutex_lock (&channel
->lock
);
113 __go_assert (i
== 0);
117 _Bool need_broadcast
;
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
);
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
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
141 if (!channel
->waiting_to_receive
|| my_wait_lock
)
145 /* Lock the channel so that we get to receive
147 was_marked
= channel
->waiting_to_receive
;
148 channel
->waiting_to_receive
= 1;
151 /* See if there is a value to receive. */
152 if (channel
->next_store
> 0)
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;
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. */
175 i
= pthread_cond_broadcast (&channel
->cond
);
176 __go_assert (i
== 0);
182 /* If there is a value on the channel, we are OK. */
183 if (channel
->next_fetch
!= channel
->next_store
)
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. */
194 __go_broadcast_to_select (channel
);
198 /* Wait for something to change, then loop around and try
201 i
= pthread_cond_wait (&channel
->cond
, &channel
->lock
);
202 __go_assert (i
== 0);
206 /* Finished receiving something on a channel. */
209 __go_receive_release (struct __go_channel
*channel
)
213 if (channel
->num_entries
!= 0)
214 channel
->next_fetch
= (channel
->next_fetch
+ 1) % channel
->num_entries
;
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
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
240 __go_unlock_and_notify_selects (struct __go_channel
*channel
)
242 pthread_mutex_t
* select_mutex
;
243 pthread_cond_t
* select_cond
;
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. */
266 __go_receive_small_closed (struct __go_channel
*channel
, _Bool for_select
,
269 uintptr_t element_size
;
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
)
288 ret
= channel
->data
[channel
->next_fetch
];
290 __go_receive_release (channel
);
292 if (received
!= NULL
)
298 /* Called by the compiler. */
301 __go_receive_small (struct __go_channel
*channel
, _Bool for_select
)
303 return __go_receive_small_closed (channel
, for_select
, NULL
);