Search code examples
gogoroutinechannel

Channel never dies although closed


In the following code, i am trying to write all files that are sent to the inputs channel and send a corresponding response over the operationOutcomes channel

main.go

package main

import(
    lr "github.com/fabulousduck/librarian"
    "fmt"
)

func main() {
    writeOpCount := 100;
    operationOutcomes, inputs := make(chan lr.WriteOpResponse), make(chan lr.WriteOp)
    go lr.WriteC(inputs, operationOutcomes)

    for i := 0; i < writeOpCount; i++ {
        inputs <- lr.WriteOp{ Dest: `../exampleFiles/createdFiles/{i}.txt`, Content: `Invoice #{i}` }
    }

    close(inputs)  

    for i := 0; i < writeOpCount; i++ {
        writeResult := <-operationOutcomes
        fmt.Println("Response from write operation : ", writeResult.Msg, "err ", writeResult.Err, "bytes written : ", writeResult.BytesWritten)
    }

    close(operationOutcomes)
}

librarian.go package librarian

import(
    "os"
    "fmt"
)

type WriteOp struct {
    Dest, Content string
}

type WriteOpResponse struct {
    Msg error
    Err bool
    BytesWritten int
}

func WriteC (inputChannel <-chan WriteOp, outputChannel chan<- WriteOpResponse) {
    workOp :=  <-inputChannel
    go writeWorker(workOp, outputChannel)
}

func writeWorker (job WriteOp, outGoing chan<- WriteOpResponse) {
    file, err := os.OpenFile(job.Dest, os.O_RDWR, 0666)
    if err != nil {
        fmt.Println("err : ", err)
        outGoing <- WriteOpResponse{ Msg: err, Err: true, BytesWritten: 0 }
    }
    bytesWritten , err := file.WriteString(job.Content)
    if err != nil {
        outGoing <- WriteOpResponse{ Msg: err, Err: true, BytesWritten: 0 }
    }
    outGoing <- WriteOpResponse{ Msg: nil, Err: false, BytesWritten: bytesWritten } 
}

This throws a deadlock in both cases when there is and isn't an error, even though i am closing both channels when i am done with them ?


Solution

  • You only read from operationOutcomes up to writeOpCount times, even though in writeWorker each execution can result in up to 3 messages being written on that channel (none of your error cases causes the function to return, to it continues processing). Because it's unbuffered and it stops being read, at some point the workers writing to it can no longer add messages and so lock forever.

    Also, since you only call WriteC once and it does not loop, it will only ever read and process one message from the inputs. If writeOpCount is > 1, then it will lock forever within the first loop when it tries to queue a second message.