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
<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.
{
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;
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,
return;
closed:
- incerr(c);
- if(pres != nil)
- *pres = true;
runtime·unlock(c);
+ runtime·panicstring("send on closed channel");
}
void
*closed = true;
c->elemalg->copy(c->elemsize, ep, nil);
c->closed |= Rclosed;
- incerr(c);
if(pres != nil)
*pres = true;
runtime·unlock(c);
if(cas->u.elemp != nil)
c->elemalg->copy(c->elemsize, cas->u.elemp, nil);
c->closed |= Rclosed;
- incerr(c);
goto retc;
syncsend:
gp = sg->g;
gp->param = sg;
runtime·ready(gp);
- goto retc;
-
-sclose:
- // send on closed channel
- incerr(c);
- goto retc;
retc:
selunlock(sel);
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);
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
}
})
- // 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
}
})
- // 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:
}
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() {
}
// 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())
}