From f58ed4e64126b595efbde9df04e63c7ea2a4fbd6 Mon Sep 17 00:00:00 2001 From: Russ Cox Date: Thu, 13 Oct 2011 16:58:04 -0400 Subject: [PATCH] gc: disallow close on receive-only channels Fixes #2353. Fixes #2246. R=golang-dev, r, gri CC=golang-dev https://golang.org/cl/5282042 --- doc/go_spec.html | 7 ++++--- src/cmd/gc/typecheck.c | 4 ++++ src/pkg/runtime/chan.c | 3 +++ test/chan/perm.go | 4 ++++ test/closedchan.go | 11 +++++++++++ 5 files changed, 26 insertions(+), 3 deletions(-) diff --git a/doc/go_spec.html b/doc/go_spec.html index 13f52996c4d..810df2c46a8 100644 --- a/doc/go_spec.html +++ b/doc/go_spec.html @@ -4531,12 +4531,13 @@ BuiltinArgs = Type [ "," ExpressionList ] | ExpressionList .

For a channel c, the built-in function close(c) -marks the channel as unable to accept more values through a send operation; -sending to or closing a closed channel causes a run-time panic. +records that no more values will be sent on the channel. +It is an error if c is a receive-only channel. +Sending to or closing a closed channel causes a run-time panic. +Closing the nil channel also causes a run-time panic. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel's type without blocking. - The multi-valued receive operation returns a received value along with an indication of whether the channel is closed.

diff --git a/src/cmd/gc/typecheck.c b/src/cmd/gc/typecheck.c index 052fc74dffc..0b2e6f0ca60 100644 --- a/src/cmd/gc/typecheck.c +++ b/src/cmd/gc/typecheck.c @@ -984,6 +984,10 @@ reswitch: yyerror("invalid operation: %#N (non-chan type %T)", n, t); goto error; } + if(!(t->chan & Csend)) { + yyerror("invalid operation: %#N (cannot close receive-only channel)", n); + goto error; + } ok |= Etop; goto ret; diff --git a/src/pkg/runtime/chan.c b/src/pkg/runtime/chan.c index cc056f65f10..475da233c13 100644 --- a/src/pkg/runtime/chan.c +++ b/src/pkg/runtime/chan.c @@ -1052,6 +1052,9 @@ runtime·closechan(Hchan *c) SudoG *sg; G* gp; + if(c == nil) + runtime·panicstring("close of nil channel"); + if(runtime·gcwaiting) runtime·gosched(); diff --git a/test/chan/perm.go b/test/chan/perm.go index 038ff94e369..af054450eab 100644 --- a/test/chan/perm.go +++ b/test/chan/perm.go @@ -48,4 +48,8 @@ func main() { case x := <-cs: // ERROR "receive" _ = x } + + close(c) + close(cs) + close(cr) // ERROR "receive" } diff --git a/test/closedchan.go b/test/closedchan.go index 95314b3345e..0dbe662d84f 100644 --- a/test/closedchan.go +++ b/test/closedchan.go @@ -327,4 +327,15 @@ func main() { testclosed(mk(closedasync())) } } + + var ch chan int + shouldPanic(func() { + close(ch) + }) + + ch = make(chan int) + close(ch) + shouldPanic(func() { + close(ch) + }) }