Bobface
Bobface

Reputation: 2952

Avoiding deadlocks in bidirectional communication between goroutines

I am making my first experiences in Go, and so far I really like the goroutine and channels construct. I am wondering if there is an idiomatic way to avoid deadlocks in bidirectional communication between multiple goroutines. Consider the following example. There are three goroutines: producer, worker and controller.

The deadlock happens when the controller tries to send a command to the worker, while the worker tries to send an integer to the controller.

producerToWorker := make(chan int)
workerToController := make(chan int)
controllerToWorker := make(chan bool) // bool represents a command for this example

// Worker
go func() {
    for {
        select {
        case i := <-producerToWorker:
            // Do some processing and send to controller
            workerToController <- (2 * i) + 1
        case <-controllerToWorker:
            // Would react to the command here
        }
    }
}()

// Controller
go func() {
    for {
        select {
        case i := <-workerToController:
            fmt.Println(i)
            if i > 180 {
                // Send a command to the worker
                controllerToWorker <- true
            }
        }
    }
}()

// Producer
for {
    producerToWorker <- rand.Intn(100)
}

Example output:

163
175
95
113
1
189 // No deadlock
23
125
179
57
149
23
91
191 // No deadlock
133
95
175
177
181 // No deadlock
17
175
63
27
181 // Deadlock!
fatal error: all goroutines are asleep - deadlock!

Buffering the channels would make this deadlock more unlikely, but not solve it logically. I would like to avoid mutexes if possible. How do you handle such situations in Go?

Edit: To give a more real-world description: I came across this problem when I tried to implement a websocket client. The websocket client (worker) connects to an external service (producer) and receives messages from it (producerToWorker) and passes them to the controller (workerToController) to handle the received messages. The controller needs to react to the received messages, for example send a response or disconnect the client when an invalid message is received (controllerToWorker).

Upvotes: 2

Views: 436

Answers (1)

Volker
Volker

Reputation: 42413

[Is there ]an idiomatic way to avoid deadlocks in bidirectional communication between multiple goroutines?

No. Nothing "idiomatic" or based on a "pattern".

How do you handle such situations in Go?

You redesign. Concurrent circular data flow is best avoided.

Upvotes: 1

Related Questions