Commit 4bfc521c by Ian Lance Taylor

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
parent 4573f2cb
...@@ -97,13 +97,9 @@ func main() { ...@@ -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 closedch <- 7
testBlock(never, func() {
for i := 0; i < 10; i++ {
closedch <- 7
}
}) })
// receiving from a non-ready channel always blocks // receiving from a non-ready channel always blocks
...@@ -189,13 +185,13 @@ func main() { ...@@ -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 { ...@@ -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) { ...@@ -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 ...@@ -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 ...@@ -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
#define SEND_NONBLOCKING_ACQUIRE_NOSPACE 1
#define SEND_NONBLOCKING_ACQUIRE_CLOSED 2
extern int __go_send_nonblocking_acquire (struct __go_channel *); extern _Bool __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 @@ ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 @@ ...@@ -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;
...@@ -29,16 +31,9 @@ __go_send_nonblocking_acquire (struct __go_channel *channel) ...@@ -29,16 +31,9 @@ __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);
__go_assert (i == 0);
__go_panic_msg ("too many operations on closed 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_CLOSED; __go_panic_msg ("send on closed channel");
} }
if (channel->num_entries > 0) if (channel->num_entries > 0)
...@@ -87,10 +82,10 @@ __go_send_nonblocking_acquire (struct __go_channel *channel) ...@@ -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) ...@@ -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 @@ ...@@ -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) ...@@ -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 ("send on closed channel");
__go_panic_msg ("too many operations 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) ...@@ -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) ...@@ -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) ...@@ -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;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment