Skip to content

Instantly share code, notes, and snippets.

@tedsuo
Created December 9, 2014 21:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tedsuo/8aca75a18d8d7607f598 to your computer and use it in GitHub Desktop.
Save tedsuo/8aca75a18d8d7607f598 to your computer and use it in GitHub Desktop.
CancellableCopy wants to stop an in-progress io.Copy, but suffers from a race
package main
import (
"bytes"
"errors"
"io"
"io/ioutil"
"os"
)
func CancellableCopy(r io.ReadCloser, cancelChan chan struct{}) error {
defer r.Close()
completeChan := make(chan error)
go func() {
completeChan <- thirdPartyConsumer(r)
}()
select {
case err := <-completeChan:
return err
case <-cancelChan:
return errors.New("canceled")
}
}
func main() {
filename := createTmpFile()
file, err := os.Open(filename)
panicOnError(err)
defer os.Remove(file.Name())
cancel := make(chan struct{})
close(cancel)
err = CancellableCopy(file, cancel)
}
func createTmpFile() string {
tmpFile, err := ioutil.TempFile("", "some-tmp-file")
panicOnError(err)
_, err = tmpFile.WriteString("this is a line of text\n")
panicOnError(err)
err = tmpFile.Close()
panicOnError(err)
return tmpFile.Name()
}
func thirdPartyConsumer(r io.Reader) error {
writer := new(bytes.Buffer)
_, err := io.Copy(writer, r)
return err
}
func panicOnError(err error) {
if err != nil {
panic(err.Error())
}
}
@tedsuo
Copy link
Author

tedsuo commented Dec 9, 2014

here's the race:

13:50 $ go run -race main.go
==================
WARNING: DATA RACE
Read by goroutine 5:
  os.(*File).read()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/os/file_unix.go:190 +0x97
  os.(*File).Read()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/os/file.go:95 +0xcb
  bytes.(*Buffer).ReadFrom()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/bytes/buffer.go:169 +0x41b
  io.Copy()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/io/io.go:349 +0x162
  main.thirdPartyConsumer()
      /Users/pivotal/go/src/race_example/main.go:54 +0x93
  main.func·001()
      /Users/pivotal/go/src/race_example/main.go:16 +0x77

Previous write by main goroutine:
  os.(*file).close()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/os/file_unix.go:108 +0x19b
  os.(*File).Close()
      /usr/local/Cellar/go/1.3.3/libexec/src/pkg/os/file_unix.go:97 +0x94
  main.CancellableCopy()
      /Users/pivotal/go/src/race_example/main.go:23 +0x2f2
  main.main()
      /Users/pivotal/go/src/race_example/main.go:36 +0x12b

Goroutine 5 (running) created at:
  main.CancellableCopy()
      /Users/pivotal/go/src/race_example/main.go:17 +0x16b
  main.main()
      /Users/pivotal/go/src/race_example/main.go:36 +0x12b
==================
Found 1 data race(s)
exit status 66

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment