mirror of git://gcc.gnu.org/git/gcc.git
Send on a closed channel panics.
Calling close on a closed channel panics. Don't limit number of receives on a closed channel. From-SVN: r171364
This commit is contained in:
parent
4573f2cb64
commit
4bfc521c9e
|
@ -97,13 +97,9 @@ func main() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// sending (a small number of times) to a closed channel is not specified
|
// sending to a closed channel panics.
|
||||||
// but the current implementation doesn't block: test that different
|
testPanic(always, func() {
|
||||||
// implementations behave the same
|
|
||||||
testBlock(never, func() {
|
|
||||||
for i := 0; i < 10; i++ {
|
|
||||||
closedch <- 7
|
closedch <- 7
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// receiving from a non-ready channel always blocks
|
// receiving from a non-ready channel always blocks
|
||||||
|
@ -189,13 +185,13 @@ func main() {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// selects with closed channels don't block
|
// selects with closed channels behave like ordinary operations
|
||||||
testBlock(never, func() {
|
testBlock(never, func() {
|
||||||
select {
|
select {
|
||||||
case <-closedch:
|
case <-closedch:
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
testBlock(never, func() {
|
testPanic(always, func() {
|
||||||
select {
|
select {
|
||||||
case closedch <- 7:
|
case closedch <- 7:
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,15 @@ func (c SChan) Impl() string {
|
||||||
return "(select)"
|
return "(select)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func shouldPanic(f func()) {
|
||||||
|
defer func() {
|
||||||
|
if recover() == nil {
|
||||||
|
panic("did not panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
func test1(c Chan) {
|
func test1(c Chan) {
|
||||||
// not closed until the close signal (a zero value) has been received.
|
// not closed until the close signal (a zero value) has been received.
|
||||||
if c.Closed() {
|
if c.Closed() {
|
||||||
|
@ -128,18 +137,15 @@ func test1(c Chan) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// send should work with ,ok too: sent a value without blocking, so ok == true.
|
// send should work with ,ok too: sent a value without blocking, so ok == true.
|
||||||
ok := c.Nbsend(1)
|
shouldPanic(func(){c.Nbsend(1)})
|
||||||
if !ok {
|
|
||||||
println("test1: send on closed got not ok", c.Impl())
|
|
||||||
}
|
|
||||||
|
|
||||||
// but the value should have been discarded.
|
// the value should have been discarded.
|
||||||
if x := c.Recv(); x != 0 {
|
if x := c.Recv(); x != 0 {
|
||||||
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
|
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
|
||||||
}
|
}
|
||||||
|
|
||||||
// similarly Send.
|
// similarly Send.
|
||||||
c.Send(2)
|
shouldPanic(func(){c.Send(2)})
|
||||||
if x := c.Recv(); x != 0 {
|
if x := c.Recv(); x != 0 {
|
||||||
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
|
println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,8 +36,6 @@ struct __go_channel
|
||||||
pthread_cond_t cond;
|
pthread_cond_t cond;
|
||||||
/* The size of elements on this channel. */
|
/* The size of elements on this channel. */
|
||||||
size_t element_size;
|
size_t element_size;
|
||||||
/* Number of operations on closed channel. */
|
|
||||||
unsigned short closed_op_count;
|
|
||||||
/* True if a goroutine is waiting to send on a synchronous
|
/* True if a goroutine is waiting to send on a synchronous
|
||||||
channel. */
|
channel. */
|
||||||
_Bool waiting_to_send;
|
_Bool waiting_to_send;
|
||||||
|
@ -84,22 +82,15 @@ struct __go_channel
|
||||||
acquired while this mutex is held. */
|
acquired while this mutex is held. */
|
||||||
extern pthread_mutex_t __go_select_data_mutex;
|
extern pthread_mutex_t __go_select_data_mutex;
|
||||||
|
|
||||||
/* Maximum permitted number of operations on a closed channel. */
|
|
||||||
#define MAX_CLOSED_OPERATIONS (0x100)
|
|
||||||
|
|
||||||
extern struct __go_channel *__go_new_channel (size_t, size_t);
|
extern struct __go_channel *__go_new_channel (size_t, size_t);
|
||||||
|
|
||||||
extern _Bool __go_synch_with_select (struct __go_channel *, _Bool);
|
extern _Bool __go_synch_with_select (struct __go_channel *, _Bool);
|
||||||
|
|
||||||
extern void __go_broadcast_to_select (struct __go_channel *);
|
extern void __go_broadcast_to_select (struct __go_channel *);
|
||||||
|
|
||||||
extern _Bool __go_send_acquire (struct __go_channel *, _Bool);
|
extern void __go_send_acquire (struct __go_channel *, _Bool);
|
||||||
|
|
||||||
#define SEND_NONBLOCKING_ACQUIRE_SPACE 0
|
extern _Bool __go_send_nonblocking_acquire (struct __go_channel *);
|
||||||
#define SEND_NONBLOCKING_ACQUIRE_NOSPACE 1
|
|
||||||
#define SEND_NONBLOCKING_ACQUIRE_CLOSED 2
|
|
||||||
|
|
||||||
extern int __go_send_nonblocking_acquire (struct __go_channel *);
|
|
||||||
|
|
||||||
extern void __go_send_release (struct __go_channel *);
|
extern void __go_send_release (struct __go_channel *);
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
license that can be found in the LICENSE file. */
|
license that can be found in the LICENSE file. */
|
||||||
|
|
||||||
#include "go-assert.h"
|
#include "go-assert.h"
|
||||||
|
#include "go-panic.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
|
||||||
/* Close a channel. After a channel is closed, sends are no longer
|
/* Close a channel. After a channel is closed, sends are no longer
|
||||||
|
@ -24,6 +25,13 @@ __go_builtin_close (struct __go_channel *channel)
|
||||||
__go_assert (i == 0);
|
__go_assert (i == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (channel->is_closed)
|
||||||
|
{
|
||||||
|
i = pthread_mutex_unlock (&channel->lock);
|
||||||
|
__go_assert (i == 0);
|
||||||
|
__go_panic_msg ("close of closed channel");
|
||||||
|
}
|
||||||
|
|
||||||
channel->is_closed = 1;
|
channel->is_closed = 1;
|
||||||
|
|
||||||
i = pthread_cond_broadcast (&channel->cond);
|
i = pthread_cond_broadcast (&channel->cond);
|
||||||
|
|
|
@ -39,7 +39,6 @@ __go_new_channel (size_t element_size, size_t entries)
|
||||||
i = pthread_cond_init (&ret->cond, NULL);
|
i = pthread_cond_init (&ret->cond, NULL);
|
||||||
__go_assert (i == 0);
|
__go_assert (i == 0);
|
||||||
ret->element_size = element_size;
|
ret->element_size = element_size;
|
||||||
ret->closed_op_count = 0;
|
|
||||||
ret->waiting_to_send = 0;
|
ret->waiting_to_send = 0;
|
||||||
ret->waiting_to_receive = 0;
|
ret->waiting_to_receive = 0;
|
||||||
ret->selected_for_send = 0;
|
ret->selected_for_send = 0;
|
||||||
|
|
|
@ -32,16 +32,6 @@ __go_receive_nonblocking_acquire (struct __go_channel *channel)
|
||||||
? channel->next_store == 0
|
? channel->next_store == 0
|
||||||
: channel->next_fetch == channel->next_store))
|
: channel->next_fetch == channel->next_store))
|
||||||
{
|
{
|
||||||
if (channel->saw_close)
|
|
||||||
{
|
|
||||||
++channel->closed_op_count;
|
|
||||||
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
|
|
||||||
{
|
|
||||||
i = pthread_mutex_unlock (&channel->lock);
|
|
||||||
__go_assert (i == 0);
|
|
||||||
__go_panic_msg ("too many operations on closed channel");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
channel->saw_close = 1;
|
channel->saw_close = 1;
|
||||||
__go_unlock_and_notify_selects (channel);
|
__go_unlock_and_notify_selects (channel);
|
||||||
return RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
|
return RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
|
||||||
|
|
|
@ -123,12 +123,6 @@ __go_receive_acquire (struct __go_channel *channel, _Bool for_select)
|
||||||
? channel->next_store == 0
|
? channel->next_store == 0
|
||||||
: channel->next_fetch == channel->next_store))
|
: channel->next_fetch == channel->next_store))
|
||||||
{
|
{
|
||||||
if (channel->saw_close)
|
|
||||||
{
|
|
||||||
++channel->closed_op_count;
|
|
||||||
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
|
|
||||||
__go_panic_msg ("too many operations on closed channel");
|
|
||||||
}
|
|
||||||
channel->saw_close = 1;
|
channel->saw_close = 1;
|
||||||
channel->selected_for_receive = 0;
|
channel->selected_for_receive = 0;
|
||||||
__go_unlock_and_notify_selects (channel);
|
__go_unlock_and_notify_selects (channel);
|
||||||
|
|
|
@ -21,8 +21,7 @@ __go_send_big (struct __go_channel* channel, const void *val, _Bool for_select)
|
||||||
alloc_size = ((channel->element_size + sizeof (uint64_t) - 1)
|
alloc_size = ((channel->element_size + sizeof (uint64_t) - 1)
|
||||||
/ sizeof (uint64_t));
|
/ sizeof (uint64_t));
|
||||||
|
|
||||||
if (!__go_send_acquire (channel, for_select))
|
__go_send_acquire (channel, for_select);
|
||||||
return;
|
|
||||||
|
|
||||||
offset = channel->next_store * alloc_size;
|
offset = channel->next_store * alloc_size;
|
||||||
__builtin_memcpy (&channel->data[offset], val, channel->element_size);
|
__builtin_memcpy (&channel->data[offset], val, channel->element_size);
|
||||||
|
|
|
@ -17,9 +17,8 @@ __go_send_nonblocking_big (struct __go_channel* channel, const void *val)
|
||||||
alloc_size = ((channel->element_size + sizeof (uint64_t) - 1)
|
alloc_size = ((channel->element_size + sizeof (uint64_t) - 1)
|
||||||
/ sizeof (uint64_t));
|
/ sizeof (uint64_t));
|
||||||
|
|
||||||
int data = __go_send_nonblocking_acquire (channel);
|
if (!__go_send_nonblocking_acquire (channel))
|
||||||
if (data != SEND_NONBLOCKING_ACQUIRE_SPACE)
|
return 0;
|
||||||
return data == SEND_NONBLOCKING_ACQUIRE_CLOSED;
|
|
||||||
|
|
||||||
offset = channel->next_store * alloc_size;
|
offset = channel->next_store * alloc_size;
|
||||||
__builtin_memcpy (&channel->data[offset], val, channel->element_size);
|
__builtin_memcpy (&channel->data[offset], val, channel->element_size);
|
||||||
|
|
|
@ -10,9 +10,11 @@
|
||||||
#include "go-panic.h"
|
#include "go-panic.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
|
||||||
/* Prepare to send something on a nonblocking channel. */
|
/* Prepare to send something on a nonblocking channel. Return true if
|
||||||
|
we acquired the channel, false if we did not acquire it because
|
||||||
|
there is no space to send a value. */
|
||||||
|
|
||||||
int
|
_Bool
|
||||||
__go_send_nonblocking_acquire (struct __go_channel *channel)
|
__go_send_nonblocking_acquire (struct __go_channel *channel)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -28,17 +30,10 @@ __go_send_nonblocking_acquire (struct __go_channel *channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel->is_closed)
|
if (channel->is_closed)
|
||||||
{
|
|
||||||
++channel->closed_op_count;
|
|
||||||
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
|
|
||||||
{
|
{
|
||||||
i = pthread_mutex_unlock (&channel->lock);
|
i = pthread_mutex_unlock (&channel->lock);
|
||||||
__go_assert (i == 0);
|
__go_assert (i == 0);
|
||||||
__go_panic_msg ("too many operations on closed channel");
|
__go_panic_msg ("send on closed channel");
|
||||||
}
|
|
||||||
i = pthread_mutex_unlock (&channel->lock);
|
|
||||||
__go_assert (i == 0);
|
|
||||||
return SEND_NONBLOCKING_ACQUIRE_CLOSED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel->num_entries > 0)
|
if (channel->num_entries > 0)
|
||||||
|
@ -87,10 +82,10 @@ __go_send_nonblocking_acquire (struct __go_channel *channel)
|
||||||
i = pthread_mutex_unlock (&channel->lock);
|
i = pthread_mutex_unlock (&channel->lock);
|
||||||
__go_assert (i == 0);
|
__go_assert (i == 0);
|
||||||
|
|
||||||
return SEND_NONBLOCKING_ACQUIRE_NOSPACE;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return SEND_NONBLOCKING_ACQUIRE_SPACE;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Send something 64 bits or smaller on a channel. */
|
/* Send something 64 bits or smaller on a channel. */
|
||||||
|
@ -100,9 +95,8 @@ __go_send_nonblocking_small (struct __go_channel *channel, uint64_t val)
|
||||||
{
|
{
|
||||||
__go_assert (channel->element_size <= sizeof (uint64_t));
|
__go_assert (channel->element_size <= sizeof (uint64_t));
|
||||||
|
|
||||||
int data = __go_send_nonblocking_acquire (channel);
|
if (!__go_send_nonblocking_acquire (channel))
|
||||||
if (data != SEND_NONBLOCKING_ACQUIRE_SPACE)
|
return 0;
|
||||||
return data == SEND_NONBLOCKING_ACQUIRE_CLOSED;
|
|
||||||
|
|
||||||
channel->data[channel->next_store] = val;
|
channel->data[channel->next_store] = val;
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,11 @@
|
||||||
#include "go-panic.h"
|
#include "go-panic.h"
|
||||||
#include "channel.h"
|
#include "channel.h"
|
||||||
|
|
||||||
/* Prepare to send something on a channel. Return true if the channel
|
/* Prepare to send something on a channel. FOR_SELECT is true if this
|
||||||
is acquired, false, if it is closed. FOR_SELECT is true if this
|
|
||||||
call is being made after a select statement returned with this
|
call is being made after a select statement returned with this
|
||||||
channel selected. */
|
channel selected. */
|
||||||
|
|
||||||
_Bool
|
void
|
||||||
__go_send_acquire (struct __go_channel *channel, _Bool for_select)
|
__go_send_acquire (struct __go_channel *channel, _Bool for_select)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
@ -25,19 +24,13 @@ __go_send_acquire (struct __go_channel *channel, _Bool for_select)
|
||||||
|
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
/* Check whether the channel is closed. */
|
|
||||||
if (channel->is_closed)
|
if (channel->is_closed)
|
||||||
{
|
{
|
||||||
++channel->closed_op_count;
|
if (for_select)
|
||||||
if (channel->closed_op_count >= MAX_CLOSED_OPERATIONS)
|
channel->selected_for_send = 0;
|
||||||
{
|
|
||||||
i = pthread_mutex_unlock (&channel->lock);
|
i = pthread_mutex_unlock (&channel->lock);
|
||||||
__go_assert (i == 0);
|
__go_assert (i == 0);
|
||||||
__go_panic_msg ("too many operations on closed channel");
|
__go_panic_msg ("send on closed channel");
|
||||||
}
|
|
||||||
channel->selected_for_send = 0;
|
|
||||||
__go_unlock_and_notify_selects (channel);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If somebody else has the channel locked for sending, we have
|
/* If somebody else has the channel locked for sending, we have
|
||||||
|
@ -54,7 +47,7 @@ __go_send_acquire (struct __go_channel *channel, _Bool for_select)
|
||||||
if (!channel->waiting_to_send)
|
if (!channel->waiting_to_send)
|
||||||
{
|
{
|
||||||
__go_assert (channel->next_store == 0);
|
__go_assert (channel->next_store == 0);
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -62,7 +55,7 @@ __go_send_acquire (struct __go_channel *channel, _Bool for_select)
|
||||||
/* If there is room on the channel, we are OK. */
|
/* If there is room on the channel, we are OK. */
|
||||||
if ((channel->next_store + 1) % channel->num_entries
|
if ((channel->next_store + 1) % channel->num_entries
|
||||||
!= channel->next_fetch)
|
!= channel->next_fetch)
|
||||||
return 1;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,8 +149,7 @@ __go_send_small (struct __go_channel *channel, uint64_t val, _Bool for_select)
|
||||||
|
|
||||||
__go_assert (channel->element_size <= sizeof (uint64_t));
|
__go_assert (channel->element_size <= sizeof (uint64_t));
|
||||||
|
|
||||||
if (!__go_send_acquire (channel, for_select))
|
__go_send_acquire (channel, for_select);
|
||||||
return;
|
|
||||||
|
|
||||||
channel->data[channel->next_store] = val;
|
channel->data[channel->next_store] = val;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue