summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/lowerings.nim24
-rw-r--r--lib/pure/concurrency/threadpool.nim22
-rw-r--r--tests/parallel/nimrod.cfg1
3 files changed, 34 insertions, 13 deletions
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index df2816a0e..af4daf785 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -205,20 +205,30 @@ proc createNimCreatePromiseCall(prom, threadParam: PNode): PNode =
   let castExpr = newNodeIT(nkCast, prom.info, prom.typ)
   castExpr.add emptyNode
   castExpr.add callCodeGenProc("nimCreatePromise", threadParam, size)
-  result = newFastAsgnStmt(prom, castExpr)
+  result = castExpr
 
 proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
                        varSection, call, barrier, prom: PNode;
                        spawnKind: TSpawnResult): PSym =
   var body = newNodeI(nkStmtList, f.info)
+  var threadLocalBarrier: PSym
   if barrier != nil:
-    body.add callCodeGenProc("barrierEnter", barrier)
+    var varSection = newNodeI(nkVarSection, barrier.info)
+    threadLocalBarrier = addLocalVar(varSection, argsParam.owner, 
+                                     barrier.typ, barrier)
+    body.add varSection
+    body.add callCodeGenProc("barrierEnter", threadLocalBarrier.newSymNode)
   var threadLocalProm: PSym
   if spawnKind == srByVar:
     threadLocalProm = addLocalVar(varSection, argsParam.owner, prom.typ, prom)
+  elif prom != nil:
+    internalAssert prom.typ.kind == tyGenericInst
+    threadLocalProm = addLocalVar(varSection, argsParam.owner, prom.typ, 
+      createNimCreatePromiseCall(prom, threadParam.newSymNode))
+    
   body.add varSection
   if prom != nil and spawnKind != srByVar:
-    body.add createNimCreatePromiseCall(prom, threadParam.newSymNode)
+    body.add newFastAsgnStmt(prom, threadLocalProm.newSymNode)
     if barrier == nil:
       body.add callCodeGenProc("nimPromiseCreateCondVar", prom)
 
@@ -230,14 +240,16 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
     if fk == promInvalid:
       localError(f.info, "cannot create a promise of type: " & 
         typeToString(prom.typ.sons[1]))
-    body.add newAsgnStmt(indirectAccess(prom,
+    body.add newAsgnStmt(indirectAccess(threadLocalProm.newSymNode,
       if fk == promGC: "data" else: "blob", prom.info), call)
     if barrier == nil:
-      body.add callCodeGenProc("nimPromiseSignal", prom)
+      # by now 'prom' is shared and thus might have beeen overwritten! we need
+      # to use the thread-local view instead:
+      body.add callCodeGenProc("nimPromiseSignal", threadLocalProm.newSymNode)
   else:
     body.add call
   if barrier != nil:
-    body.add callCodeGenProc("barrierLeave", barrier)
+    body.add callCodeGenProc("barrierLeave", threadLocalBarrier.newSymNode)
 
   var params = newNodeI(nkFormalParams, f.info)
   params.add emptyNode
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 24cb9ccdd..22f00bc0d 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -42,22 +42,30 @@ proc signal(cv: var CondVar) =
 
 type
   Barrier* {.compilerProc.} = object
-    counter: int
+    entered: int
     cv: CondVar
+    cacheAlign: array[0..20, byte] # ensure 'left' is not on the same
+                                   # cache line as 'entered'
+    left: int
 
 proc barrierEnter*(b: ptr Barrier) {.compilerProc.} =
-  atomicInc b.counter
+  atomicInc b.entered
 
 proc barrierLeave*(b: ptr Barrier) {.compilerProc.} =
-  atomicDec b.counter
-  if b.counter <= 0: signal(b.cv)
+  atomicInc b.left
+  # these can only be equal if 'closeBarrier' already signaled its interest
+  # in this event:
+  if b.left == b.entered: signal(b.cv)
 
 proc openBarrier*(b: ptr Barrier) {.compilerProc.} =
-  b.counter = 0
+  b.entered = 0
   b.cv = createCondVar()
+  b.left = -1
 
 proc closeBarrier*(b: ptr Barrier) {.compilerProc.} =
-  while b.counter > 0: await(b.cv)
+  # signal interest in the "all done" event:
+  atomicInc b.left
+  while b.left != b.entered: await(b.cv)
   destroyCondVar(b.cv)
 
 {.pop.}
@@ -151,7 +159,7 @@ proc await*[T](prom: Promise[T]) =
   if prom.usesCondVar: await(prom.cv)
 
 proc awaitAndThen*[T](prom: Promise[T]; action: proc (x: T) {.closure.}) =
-  ## blocks until the value is available and then passes this value
+  ## blocks until the ``prom`` is available and then passes its value
   ## to ``action``. Note that due to Nimrod's parameter passing semantics this
   ## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
   ## sometimes be more efficient than ``^``.
diff --git a/tests/parallel/nimrod.cfg b/tests/parallel/nimrod.cfg
new file mode 100644
index 000000000..b81c89721
--- /dev/null
+++ b/tests/parallel/nimrod.cfg
@@ -0,0 +1 @@
+threads:on