Simplified concurrency mechanisms for goroutines
It does not longer make sense to have a mutex and cancelled context check since we have a wait group and channels with buffers with the size equal the number of workers. Without the wait group we had to make sure no goroutine tries to write to the closed channel. Now, channel is closed after all goroutines completed (either by finding prime or cancelling the work), so there is no chance someone tries to write to closed channel.
This commit is contained in:
1 file changed
+4
-17
+4
-17
@@ -74,7 +74,6 @@ func GenerateSafePrime(
|
|||||||
defer close(primeChan)
|
defer close(primeChan)
|
||||||
defer close(errChan)
|
defer close(errChan)
|
||||||
|
|
||||||
mutex := &sync.Mutex{}
|
|
||||||
waitGroup := &sync.WaitGroup{}
|
waitGroup := &sync.WaitGroup{}
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
@@ -82,28 +81,22 @@ func GenerateSafePrime(
|
|||||||
for i := 0; i < concurrencyLevel; i++ {
|
for i := 0; i < concurrencyLevel; i++ {
|
||||||
waitGroup.Add(1)
|
waitGroup.Add(1)
|
||||||
runGenPrimeRoutine(
|
runGenPrimeRoutine(
|
||||||
ctx, primeChan, errChan, mutex, waitGroup, random, bitLen,
|
ctx, primeChan, errChan, waitGroup, random, bitLen,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel after the specified timeout.
|
// Cancel after the specified timeout.
|
||||||
go func() {
|
go func() {
|
||||||
time.Sleep(timeout)
|
time.Sleep(timeout)
|
||||||
mutex.Lock()
|
|
||||||
cancel()
|
cancel()
|
||||||
mutex.Unlock()
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case result := <-primeChan:
|
case result := <-primeChan:
|
||||||
mutex.Lock()
|
|
||||||
cancel()
|
cancel()
|
||||||
mutex.Unlock()
|
|
||||||
p, q, err = result.p, result.q, nil
|
p, q, err = result.p, result.q, nil
|
||||||
case err := <-errChan:
|
case err := <-errChan:
|
||||||
mutex.Lock()
|
|
||||||
cancel()
|
cancel()
|
||||||
mutex.Unlock()
|
|
||||||
p, q, err = nil, nil, err
|
p, q, err = nil, nil, err
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
p, q, err = nil, nil, fmt.Errorf("generator timed out after %v", timeout)
|
p, q, err = nil, nil, fmt.Errorf("generator timed out after %v", timeout)
|
||||||
@@ -157,7 +150,6 @@ func runGenPrimeRoutine(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
primeChan chan safePrime,
|
primeChan chan safePrime,
|
||||||
errChan chan error,
|
errChan chan error,
|
||||||
mutex *sync.Mutex,
|
|
||||||
waitGroup *sync.WaitGroup,
|
waitGroup *sync.WaitGroup,
|
||||||
rand io.Reader,
|
rand io.Reader,
|
||||||
pBitLen int,
|
pBitLen int,
|
||||||
@@ -175,10 +167,11 @@ func runGenPrimeRoutine(
|
|||||||
bigMod := new(big.Int)
|
bigMod := new(big.Int)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
|
defer waitGroup.Done()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
waitGroup.Done()
|
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
_, err := io.ReadFull(rand, bytes)
|
_, err := io.ReadFull(rand, bytes)
|
||||||
@@ -266,13 +259,7 @@ func runGenPrimeRoutine(
|
|||||||
isPocklingtonCriterionSatisfied(p) &&
|
isPocklingtonCriterionSatisfied(p) &&
|
||||||
q.BitLen() == qBitLen {
|
q.BitLen() == qBitLen {
|
||||||
|
|
||||||
mutex.Lock()
|
primeChan <- safePrime{p, q}
|
||||||
if ctx.Err() == nil {
|
|
||||||
primeChan <- safePrime{p, q}
|
|
||||||
}
|
|
||||||
mutex.Unlock()
|
|
||||||
|
|
||||||
waitGroup.Done()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user