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:
parent
0a5fc261b6
commit
27c74d3499
@ -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.
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
}
|
}
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user