VPavliashvili
VPavliashvili

Reputation: 655

io.Pipe and deadlock when trying to write/read

I tried to understand underlying logic for several hours but no progress. This code below returns deadlock after 1st iteration. If I close writer before io.Copy than deadlock disappears but nothing is printed(since pipe write end is closed before read)

func main() {
    reader, writer := io.Pipe()
    c := make(chan string)

    go func() {
        for i := 0; i < 5; i++ {
            text := fmt.Sprintf("hello %vth time", i+1)
            c <- text
        }

        close(c)
    }()

    for msg := range c {
        msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)

        go fmt.Fprint(writer, msg)
        io.Copy(os.Stdout, reader)
        writer.Close()
    }

}

and this is the error after running the code

received from channel -> hello 1th time fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select]: io.(*pipe).read(0xc000130120, {0xc00013e000, 0x8000, 0xc00011e001?}) /usr/lib/go/src/io/pipe.go:57 +0xb1 io.(*PipeReader).Read(0x0?, {0xc00013e000?, 0xc00011e050?, 0x10?}) /usr/lib/go/src/io/pipe.go:136 +0x25 io.copyBuffer({0x4bde98, 0xc00011e050}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0}) /usr/lib/go/src/io/io.go:427 +0x1b2 io.Copy(...) /usr/lib/go/src/io/io.go:386 os.genericReadFrom(0x101c00002c500?, {0x4bddb8, 0xc00012e018}) /usr/lib/go/src/os/file.go:161 +0x67 os.(*File).ReadFrom(0xc00012e008, {0x4bddb8, 0xc00012e018}) /usr/lib/go/src/os/file.go:155 +0x1b0 io.copyBuffer({0x4bde38, 0xc00012e008}, {0x4bddb8, 0xc00012e018}, {0x0, 0x0, 0x0}) /usr/lib/go/src/io/io.go:413 +0x14b io.Copy(...) /usr/lib/go/src/io/io.go:386 main.pipetest() /home/stranger/source-code/golang/ipctest/pipes/main.go:39 +0x1ae main.main() /home/stranger/source-code/golang/ipctest/pipes/main.go:10 +0x17

goroutine 18 [chan send]: main.pipetest.func1() /home/stranger/source-code/golang/ipctest/pipes/main.go:29 +0x85 created by main.pipetest /home/stranger/source-code/golang/ipctest/pipes/main.go:26 +0x17a exit status 2

Upvotes: 1

Views: 1052

Answers (1)

Hymns For Disco
Hymns For Disco

Reputation: 8395

io.Copy keeps trying to copy until reader reaches EOF (in this case, when the pipe is closed). Since you call writer.Close() after io.Copy ends, io.Copy will never see that EOF, and hangs forever.

The other problem with your code is that you're trying to close the pipe multiple times (each time the loop code repeats). In general Closeable objects should only be closed once, and are assumed to be un-usable after being Closed. If you need to re-use them, you should create a new instance.

Here's a working revision of your code:

func main() {
    c := make(chan string)

    go func() {
        for i := 0; i < 5; i++ {
            text := fmt.Sprintf("hello %vth time", i+1)
            c <- text
        }

        close(c)
    }()

    for msg := range c {
        msg = fmt.Sprintf("\nreceived from channel -> %v\n", msg)

        // Create a new pipe for this message.
        reader, writer := io.Pipe()
        go func() {
            fmt.Fprint(writer, msg)
            // Close the pipe after writing the message.
            writer.Close()
        }()

        io.Copy(os.Stdout, reader)
    }
}

Upvotes: 1

Related Questions