1
0
mirror of https://github.com/golang/go synced 2024-11-11 20:50:23 -07:00

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
This commit is contained in:
Russ Cox 2011-01-21 15:07:13 -05:00
parent 0a5fc261b6
commit 27c74d3499
4 changed files with 30 additions and 40 deletions

View File

@ -4394,7 +4394,7 @@ BuiltinArgs = Type [ "," ExpressionList ] | ExpressionList .
<p> <p>
For a channel <code>c</code>, the built-in function <code>close(c)</code> 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; 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 After calling <code>close</code>, and after any previously
sent values have been received, receive operations will return sent values have been received, receive operations will return
the zero value for the channel's type without blocking. the zero value for the channel's type without blocking.

View File

@ -11,8 +11,6 @@ enum
{ {
Wclosed = 0x0001, // writer has closed Wclosed = 0x0001, // writer has closed
Rclosed = 0x0002, // reader has seen close Rclosed = 0x0002, // reader has seen close
Eincr = 0x0004, // increment errors
Emax = 0x0800, // error limit before throw
}; };
typedef struct Link Link; typedef struct Link Link;
@ -151,16 +149,6 @@ runtime·makechan(Type *elem, int64 hint, Hchan *ret)
FLUSH(&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 * generic single channel send/recv
* if the bool pointer is nil, * if the bool pointer is nil,
@ -276,10 +264,8 @@ asynch:
return; return;
closed: closed:
incerr(c);
if(pres != nil)
*pres = true;
runtime·unlock(c); runtime·unlock(c);
runtime·panicstring("send on closed channel");
} }
void void
@ -393,7 +379,6 @@ closed:
*closed = true; *closed = true;
c->elemalg->copy(c->elemsize, ep, nil); c->elemalg->copy(c->elemsize, ep, nil);
c->closed |= Rclosed; c->closed |= Rclosed;
incerr(c);
if(pres != nil) if(pres != nil)
*pres = true; *pres = true;
runtime·unlock(c); runtime·unlock(c);
@ -863,7 +848,6 @@ rclose:
if(cas->u.elemp != nil) if(cas->u.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.elemp, nil); c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
c->closed |= Rclosed; c->closed |= Rclosed;
incerr(c);
goto retc; goto retc;
syncsend: syncsend:
@ -876,12 +860,6 @@ syncsend:
gp = sg->g; gp = sg->g;
gp->param = sg; gp->param = sg;
runtime·ready(gp); runtime·ready(gp);
goto retc;
sclose:
// send on closed channel
incerr(c);
goto retc;
retc: retc:
selunlock(sel); selunlock(sel);
@ -891,6 +869,12 @@ retc:
as = (byte*)&sel + cas->so; as = (byte*)&sel + cas->so;
freesel(sel); freesel(sel);
*as = true; *as = true;
return;
sclose:
// send on closed channel
selunlock(sel);
runtime·panicstring("send on closed channel");
} }
// closechan(sel *byte); // closechan(sel *byte);
@ -904,7 +888,11 @@ runtime·closechan(Hchan *c)
runtime·gosched(); runtime·gosched();
runtime·lock(c); runtime·lock(c);
incerr(c); if(c->closed & Wclosed) {
runtime·unlock(c);
runtime·panicstring("close of closed channel");
}
c->closed |= Wclosed; c->closed |= Wclosed;
// release all readers // release all readers

View File

@ -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:
} }

View File

@ -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())
} }