From 530e40c1d41af56662b1e07487325f00e496261d Mon Sep 17 00:00:00 2001 From: Piotr Dyraga Date: Mon, 30 Jul 2018 09:56:40 +0200 Subject: [PATCH] Deadlock fixed - result channel buffer increased Although the result channel send operation and context cancel are guarded by the same mutex and context is cancelled by the main thread when it receives a result message from the channnel, there is absolutely no guarantee that one goroutine can't write result to the channel after the first goroutine did the same and before the main thread cancelled the context. This is the following scenario: - Worker #1 acquires mutex, writes to the result channel - Worker #2 acquires mutex, writes to the result channel - Main thread reads from the result channel, acquires mutex, cancels the context Since our result channel (and error channel) had buffer of size 1, the Worker #2 was hanging indefinitely. Setting the buffer size to the number of goroutines fixed the problem. Alternative scenario was: - Worker #1 acquires mutex, writes to the result channel - Main thread acquires mutex, reads from the result channel, cancels the context - Worker #2 receives cancel notification so it completes without yielding the result That's why deadlock was happening only from time to time. --- safe_prime_generator.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/safe_prime_generator.go b/safe_prime_generator.go index ea677eb..3a3355f 100644 --- a/safe_prime_generator.go +++ b/safe_prime_generator.go @@ -68,19 +68,19 @@ func GenerateSafePrime( return nil, nil, errors.New("safe prime size must be at least 6 bits") } - primeChan := make(chan safePrime, 1) - errChan := make(chan error, 1) + primeChan := make(chan safePrime, concurrencyLevel) + errChan := make(chan error, concurrencyLevel) defer close(primeChan) defer close(errChan) mutex := &sync.Mutex{} waitGroup := &sync.WaitGroup{} - waitGroup.Add(concurrencyLevel) ctx, cancel := context.WithCancel(context.Background()) for i := 0; i < concurrencyLevel; i++ { + waitGroup.Add(1) runGenPrimeRoutine( ctx, primeChan, errChan, mutex, waitGroup, random, bitLen, )