Commit 27c74d34 authored by Russ Cox's avatar Russ Cox

spec, runtime, tests: send on closed channel panics

Close of closed channel panics.
Receive from closed channel never panics,
even if done repeatedly.

Fixes #1349.
Fixes #1419.

R=gri, iant, ken2, r, gri1, r2, iant2, rog, albert.strasheim, niemeyer, ejsherry
CC=golang-dev
https://golang.org/cl/3989042
parent 0a5fc261
......@@ -4394,7 +4394,7 @@ BuiltinArgs = Type [ "," ExpressionList ] | ExpressionList .
<p>
For a channel <code>c</code>, the built-in function <code>close(c)</code>
marks the channel as unable to accept more values through a send operation;
values sent to a closed channel are ignored.
sending to or closing a closed channel causes a <a href="#Run_time_panics">run-time panic</a>.
After calling <code>close</code>, and after any previously
sent values have been received, receive operations will return
the zero value for the channel's type without blocking.
......
......@@ -11,8 +11,6 @@ enum
{
Wclosed = 0x0001, // writer has closed
Rclosed = 0x0002, // reader has seen close
Eincr = 0x0004, // increment errors
Emax = 0x0800, // error limit before throw
};
typedef struct Link Link;
......@@ -151,16 +149,6 @@ runtime·makechan(Type *elem, int64 hint, Hchan *ret)
FLUSH(&ret);
}
static void
incerr(Hchan* c)
{
c->closed += Eincr;
if(c->closed & Emax) {
// Note that channel locks may still be held at this point.
runtime·throw("too many operations on a closed channel");
}
}
/*
* generic single channel send/recv
* if the bool pointer is nil,
......@@ -276,10 +264,8 @@ asynch:
return;
closed:
incerr(c);
if(pres != nil)
*pres = true;
runtime·unlock(c);
runtime·panicstring("send on closed channel");
}
void
......@@ -393,7 +379,6 @@ closed:
*closed = true;
c->elemalg->copy(c->elemsize, ep, nil);
c->closed |= Rclosed;
incerr(c);
if(pres != nil)
*pres = true;
runtime·unlock(c);
......@@ -863,7 +848,6 @@ rclose:
if(cas->u.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
c->closed |= Rclosed;
incerr(c);
goto retc;
syncsend:
......@@ -876,12 +860,6 @@ syncsend:
gp = sg->g;
gp->param = sg;
runtime·ready(gp);
goto retc;
sclose:
// send on closed channel
incerr(c);
goto retc;
retc:
selunlock(sel);
......@@ -891,6 +869,12 @@ retc:
as = (byte*)&sel + cas->so;
freesel(sel);
*as = true;
return;
sclose:
// send on closed channel
selunlock(sel);
runtime·panicstring("send on closed channel");
}
// closechan(sel *byte);
......@@ -904,7 +888,11 @@ runtime·closechan(Hchan *c)
runtime·gosched();
runtime·lock(c);
incerr(c);
if(c->closed & Wclosed) {
runtime·unlock(c);
runtime·panicstring("close of closed channel");
}
c->closed |= Wclosed;
// release all readers
......
......@@ -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:
}
......
......@@ -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())
}
......
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