summary refs log tree commit diff stats
path: root/lib/pure/concurrency
diff options
context:
space:
mode:
authorDmitry Polienko <polienko.dv@gmail.com>2016-08-24 22:01:37 +0700
committerDmitry Polienko <polienko.dv@gmail.com>2016-08-24 22:01:37 +0700
commit885543e43e67b4f27e01d1b55fa239e33664fec9 (patch)
tree9119337319cdb36477b3363e4de53620701974a9 /lib/pure/concurrency
parent2edd3786ce71e1a15fd9de0dd32452ff0996e210 (diff)
downloadNim-885543e43e67b4f27e01d1b55fa239e33664fec9.tar.gz
Add a lock to prevent race condition
Diffstat (limited to 'lib/pure/concurrency')
-rw-r--r--lib/pure/concurrency/threadpool.nim55
1 files changed, 33 insertions, 22 deletions
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index baa96cacd..8cdb83e19 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -300,10 +300,13 @@ var
 # A workaround for recursion deadlock issue
 # https://github.com/nim-lang/Nim/issues/4597
 var
-  numSlavesRunning: int
-  numSlavesWaiting: int
+  numSlavesLock: Lock
+  numSlavesRunning {.guard: numSlavesLock}: int
+  numSlavesWaiting {.guard: numSlavesLock}: int
   isSlave {.threadvar.}: bool
 
+numSlavesLock.initLock
+
 gSomeReady.initSemaphore()
 
 proc slave(w: ptr Worker) {.thread.} =
@@ -320,9 +323,13 @@ proc slave(w: ptr Worker) {.thread.} =
     # in Visual Studio?)
     when not defined(vcc): assert(not w.ready)
 
-    atomicInc numSlavesRunning
+    withLock numSlavesLock:
+      inc numSlavesRunning
+
     w.f(w, w.data)
-    atomicDec numSlavesRunning
+
+    withLock numSlavesLock:
+      dec numSlavesRunning
 
     if w.q.len != 0: w.cleanFlowVars
     if w.shutdown:
@@ -477,29 +484,33 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
         await(self.taskStarted)
         return
 
-    if isSlave and numSlavesRunning <= numSlavesWaiting + 1:
-      # All the other slaves are waiting
-      # If we wait now, we-re deadlocked until
-      # an external spawn happens !
-      if currentPoolSize < maxPoolSize:
-        if not workersData[currentPoolSize].initialized:
-          activateWorkerThread(currentPoolSize)
-        let w = addr(workersData[currentPoolSize])
-        atomicInc currentPoolSize
-        if selectWorker(w, fn, data):
-          return
-      else:
-        # There is no place in pool. We're deadlocked.
-        # echo "Deadlock!"
-        discard
-
     if isSlave:
-      atomicInc numSlavesWaiting
+      # Run under lock until `numSlavesWaiting` increment to avoid a
+      # race (otherwise two last threads might start waiting together)
+      withLock numSlavesLock:
+        if numSlavesRunning <= numSlavesWaiting + 1:
+          # All the other slaves are waiting
+          # If we wait now, we-re deadlocked until
+          # an external spawn happens !
+          if currentPoolSize < maxPoolSize:
+            if not workersData[currentPoolSize].initialized:
+              activateWorkerThread(currentPoolSize)
+            let w = addr(workersData[currentPoolSize])
+            atomicInc currentPoolSize
+            if selectWorker(w, fn, data):
+              return
+          else:
+            # There is no place in the pool. We're deadlocked.
+            # echo "Deadlock!"
+            discard
+
+        inc numSlavesWaiting
 
     await(gSomeReady)
 
     if isSlave:
-      atomicDec numSlavesWaiting
+      withLock numSlavesLock:
+        dec numSlavesWaiting
 
 var
   distinguishedLock: Lock