]> Cypherpunks repositories - gostls13.git/commitdiff
io: Avoid another race condition in pipes.
authorIan Lance Taylor <iant@golang.org>
Wed, 21 Jul 2010 17:57:46 +0000 (10:57 -0700)
committerIan Lance Taylor <iant@golang.org>
Wed, 21 Jul 2010 17:57:46 +0000 (10:57 -0700)
Goroutine 1:
  Call Read on read half of pipe, entering pipeHalf.rw.
  Check ioclosed field, which is false.
  Send data to p.c1
  Wait for response on p.c2.

Goroutine 2:
  Call Close on read half of pipe, entering pipeHalf.close.
  Set closed field.
  Send error to p.cclose.
  Set ioclosed field.
  Send 1 to p.done.
  Return and exit goroutine.

Goroutine 3:
  This is the goroutine running pipe.run, and for some reason
  it has started late.
  Read error from p.rclose; set rerr and continue.
  Read 1 from p.done; increment ndone and continue.
  Read data from r1 (sent by goroutine 1); set r1 = nil and continue

Now goroutine 1 is waiting for a response, and goroutine 3 is
waiting for something else to happen.

This patch fixes the race by having the runner check whether
the read half is closed when it is asked for read data, and
similarly for the corresponding race on the write half.

This patch also fixes the similar race in which ndone gets
bumped up to 2 while there is a reader or writer waiting.

There is still another race to fix.  It is possible for the
read half and the write half to both be closed, and for the
runner goroutine to exit, all before the runner goroutine sees
the request from a reader.  E.g., in the above, have goroutine
2 also close the write half, and have goroutine 3 see both
done messages before it sees the request from goroutine 1.

R=rsc
CC=golang-dev
https://golang.org/cl/1862045

src/pkg/io/pipe.go

index 898526921fcfc8a326e5fac2cd0c4150d798b85f..ad949cc6c5a18b066b8f9cffdd9a48dbf16e9437 100644 (file)
@@ -52,6 +52,13 @@ func (p *pipe) run() {
                case <-p.done:
                        if ndone++; ndone == 2 {
                                // both reader and writer are gone
+                               // close out any existing i/o
+                               if r1 == nil {
+                                       p.r2 <- pipeResult{0, os.EINVAL}
+                               }
+                               if w1 == nil {
+                                       p.w2 <- pipeResult{0, os.EINVAL}
+                               }
                                return
                        }
                        continue
@@ -89,6 +96,11 @@ func (p *pipe) run() {
                                p.r2 <- pipeResult{0, werr}
                                continue
                        }
+                       if rerr != nil {
+                               // read end is closed
+                               p.r2 <- pipeResult{0, os.EINVAL}
+                               continue
+                       }
                        r1 = nil // disable Read until this one is done
                case wb = <-w1:
                        if rerr != nil {
@@ -96,6 +108,11 @@ func (p *pipe) run() {
                                p.w2 <- pipeResult{0, rerr}
                                continue
                        }
+                       if werr != nil {
+                               // write end is closed
+                               p.w2 <- pipeResult{0, os.EINVAL}
+                               continue
+                       }
                        w1 = nil // disable Write until this one is done
                }