Send on a closed channel panics.
authorIan Lance Taylor <ian@gcc.gnu.org>
Wed, 23 Mar 2011 21:13:57 +0000 (21:13 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Wed, 23 Mar 2011 21:13:57 +0000 (21:13 +0000)
Calling close on a closed channel panics.
Don't limit number of receives on a closed channel.

From-SVN: r171364

gcc/testsuite/go.test/test/chan/select3.go
gcc/testsuite/go.test/test/closedchan.go
libgo/runtime/channel.h
libgo/runtime/go-close.c
libgo/runtime/go-new-channel.c
libgo/runtime/go-rec-nb-small.c
libgo/runtime/go-rec-small.c
libgo/runtime/go-send-big.c
libgo/runtime/go-send-nb-big.c
libgo/runtime/go-send-nb-small.c
libgo/runtime/go-send-small.c

index a1a2ef50b56ed38ca935e3bf013406728779c41b..9877b12a98ce04e1f481453d0809f356fa8afae4 100644 (file)
@@ -97,13 +97,9 @@ func main() {
                }
        })
 
-       // sending (a small number of times) to a closed channel is not specified
-       // but the current implementation doesn't block: test that different
-       // implementations behave the same
-       testBlock(never, func() {
-               for i := 0; i < 10; i++ {
-                       closedch <- 7
-               }
+       // sending to a closed channel panics.
+       testPanic(always, func() {
+               closedch <- 7
        })
 
        // 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() {
                select {
                case <-closedch:
                }
        })
-       testBlock(never, func() {
+       testPanic(always, func() {
                select {
                case closedch <- 7:
                }
index c7c759be3b3edfcad980be8dd0155493db7f7aab..8126d5a4e4c71ade746f8e836d7f25451cf19d73 100644 (file)
@@ -100,6 +100,15 @@ func (c SChan) Impl() string {
        return "(select)"
 }
 
+func shouldPanic(f func()) {
+       defer func() {
+               if recover() == nil {
+                       panic("did not panic")
+               }
+       }()
+       f()
+}
+
 func test1(c Chan) {
        // not closed until the close signal (a zero value) has been received.
        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.
-       ok := c.Nbsend(1)
-       if !ok {
-               println("test1: send on closed got not ok", c.Impl())
-       }
+       shouldPanic(func(){c.Nbsend(1)})
 
-       // but the value should have been discarded.
+       // the value should have been discarded.
        if x := c.Recv(); x != 0 {
                println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
        }
 
        // similarly Send.
-       c.Send(2)
+       shouldPanic(func(){c.Send(2)})
        if x := c.Recv(); x != 0 {
                println("test1: recv on closed got non-zero after send on closed:", x, c.Impl())
        }
index b0d13477a1cd83631da36590175773093a7014b5..5d2f49f1ee830594cb654687a504d3394f070a9e 100644 (file)
@@ -36,8 +36,6 @@ struct __go_channel
   pthread_cond_t cond;
   /* The size of elements on this channel.  */
   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
      channel.  */
   _Bool waiting_to_send;
@@ -84,22 +82,15 @@ struct __go_channel
    acquired while this mutex is held.  */
 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 _Bool __go_synch_with_select (struct __go_channel *, _Bool);
 
 extern void __go_broadcast_to_select (struct __go_channel *);
 
-extern _Bool __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 void __go_send_acquire (struct __go_channel *, _Bool);
 
-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 *);
 
index ced742985f6e0b93c8b1fd01edb3a51acfee1859..44533ebe4c7ff96439d80b97d5a0fcb3b6e6acaf 100644 (file)
@@ -5,6 +5,7 @@
    license that can be found in the LICENSE file.  */
 
 #include "go-assert.h"
+#include "go-panic.h"
 #include "channel.h"
 
 /* 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);
     }
 
+  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;
 
   i = pthread_cond_broadcast (&channel->cond);
index d57f52c6c152b9cfe8a11138c09c7212beb94141..d16bde62d75a4df534230be79f07dc541e5c4103 100644 (file)
@@ -39,7 +39,6 @@ __go_new_channel (size_t element_size, size_t entries)
   i = pthread_cond_init (&ret->cond, NULL);
   __go_assert (i == 0);
   ret->element_size = element_size;
-  ret->closed_op_count = 0;
   ret->waiting_to_send = 0;
   ret->waiting_to_receive = 0;
   ret->selected_for_send = 0;
index 9983d34644d1cf7fee0298e3accfbd00b8e33062..d77a2ace432676e8b95d9fb93d8aef74c7dc056f 100644 (file)
@@ -32,16 +32,6 @@ __go_receive_nonblocking_acquire (struct __go_channel *channel)
          ? channel->next_store == 0
          : 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;
       __go_unlock_and_notify_selects (channel);
       return RECEIVE_NONBLOCKING_ACQUIRE_CLOSED;
index 765e8d310de07af06bf83acae6c6cf994ea473ea..c4dc8b6e892e40518f63389143a533f14feb0353 100644 (file)
@@ -123,12 +123,6 @@ __go_receive_acquire (struct __go_channel *channel, _Bool for_select)
              ? channel->next_store == 0
              : 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->selected_for_receive = 0;
          __go_unlock_and_notify_selects (channel);
index f58ffb6c82d36be2380bdd6e166751257a13d424..c2732639536f1ca0bb47cb8695cc6ff1611a6353 100644 (file)
@@ -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)
                / sizeof (uint64_t));
 
-  if (!__go_send_acquire (channel, for_select))
-    return;
+  __go_send_acquire (channel, for_select);
 
   offset = channel->next_store * alloc_size;
   __builtin_memcpy (&channel->data[offset], val, channel->element_size);
index 288ce7f4419f4133ef10166e5b1562abd84a4db7..1d33dd6207baa75055979850f7a07c3382b56346 100644 (file)
@@ -17,9 +17,8 @@ __go_send_nonblocking_big (struct __go_channel* channel, const void *val)
   alloc_size = ((channel->element_size + sizeof (uint64_t) - 1)
                / sizeof (uint64_t));
 
-  int data = __go_send_nonblocking_acquire (channel);
-  if (data != SEND_NONBLOCKING_ACQUIRE_SPACE)
-    return data == SEND_NONBLOCKING_ACQUIRE_CLOSED;
+  if (!__go_send_nonblocking_acquire (channel))
+    return 0;
 
   offset = channel->next_store * alloc_size;
   __builtin_memcpy (&channel->data[offset], val, channel->element_size);
index f23ae016433b15d1ebb95a9983895f551ab3f78d..5c49a67ffb4093518223b5109f67407beae2e113 100644 (file)
 #include "go-panic.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)
 {
   int i;
@@ -29,16 +31,9 @@ __go_send_nonblocking_acquire (struct __go_channel *channel)
 
   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);
       __go_assert (i == 0);
-      return SEND_NONBLOCKING_ACQUIRE_CLOSED;
+      __go_panic_msg ("send on closed channel");
     }
 
   if (channel->num_entries > 0)
@@ -87,10 +82,10 @@ __go_send_nonblocking_acquire (struct __go_channel *channel)
       i = pthread_mutex_unlock (&channel->lock);
       __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.  */
@@ -100,9 +95,8 @@ __go_send_nonblocking_small (struct __go_channel *channel, uint64_t val)
 {
   __go_assert (channel->element_size <= sizeof (uint64_t));
 
-  int data = __go_send_nonblocking_acquire (channel);
-  if (data != SEND_NONBLOCKING_ACQUIRE_SPACE)
-    return data == SEND_NONBLOCKING_ACQUIRE_CLOSED;
+  if (!__go_send_nonblocking_acquire (channel))
+    return 0;
 
   channel->data[channel->next_store] = val;
 
index 506c90e648d099ca44003c7d15b435cf122a6af3..56f9470911f37fd2223da1785d7f594803364c3b 100644 (file)
 #include "go-panic.h"
 #include "channel.h"
 
-/* Prepare to send something on a channel.  Return true if the channel
-   is acquired, false, if it is closed.  FOR_SELECT is true if this
+/* Prepare to send something on a channel.  FOR_SELECT is true if this
    call is being made after a select statement returned with this
    channel selected.  */
 
-_Bool
+void
 __go_send_acquire (struct __go_channel *channel, _Bool for_select)
 {
   int i;
@@ -25,19 +24,13 @@ __go_send_acquire (struct __go_channel *channel, _Bool for_select)
 
   while (1)
     {
-      /* Check whether the 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");
-           }
-         channel->selected_for_send = 0;
-         __go_unlock_and_notify_selects (channel);
-         return 0;
+         if (for_select)
+           channel->selected_for_send = 0;
+         i = pthread_mutex_unlock (&channel->lock);
+         __go_assert (i == 0);
+         __go_panic_msg ("send on closed channel");
        }
 
       /* 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)
                {
                  __go_assert (channel->next_store == 0);
-                 return 1;
+                 return;
                }
            }
          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 ((channel->next_store + 1) % channel->num_entries
                  != 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));
 
-  if (!__go_send_acquire (channel, for_select))
-    return;
+  __go_send_acquire (channel, for_select);
 
   channel->data[channel->next_store] = val;