summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/lowerings.nim27
-rw-r--r--compiler/semexprs.nim12
-rw-r--r--compiler/semmagic.nim11
-rw-r--r--lib/pure/concurrency/threadpool.nim33
-rw-r--r--tests/parallel/tflowvar.nim17
-rw-r--r--tests/parallel/tsysspawn.nim (renamed from tests/system/tsysspawn.nim)0
-rw-r--r--tests/parallel/tsysspawnbadarg.nim (renamed from tests/system/tsysspawnbadarg.nim)2
8 files changed, 68 insertions, 36 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index 58b01d5e8..c47407ee2 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -605,7 +605,7 @@ const
   # thus cannot be overloaded (also documented in the spec!):
   SpecialSemMagics* = {
     mDefined, mDefinedInScope, mCompiles, mLow, mHigh, mSizeOf, mIs, mOf, 
-    mEcho, mShallowCopy, mExpandToAst, mParallel}
+    mEcho, mShallowCopy, mExpandToAst, mParallel, mSpawn}
 
 type
   PNode* = ref TNode
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 2a1a8e577..047bdf832 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -86,8 +86,14 @@ proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
   # returns a[].b as a node
   var deref = newNodeI(nkHiddenDeref, info)
   deref.typ = a.typ.skipTypes(abstractInst).sons[0]
-  assert deref.typ.kind == tyObject
-  let field = getSymFromList(deref.typ.n, getIdent(b))
+  var t = deref.typ
+  var field: PSym
+  while true:
+    assert t.kind == tyObject
+    field = getSymFromList(t.n, getIdent(b))
+    if field != nil: break
+    t = t.sons[0]
+    if t == nil: break
   assert field != nil, b
   addSon(deref, a)
   result = newNodeI(nkDotExpr, info)
@@ -124,6 +130,7 @@ proc callCodegenProc*(name: string, arg1: PNode;
     result.add arg1
     if arg2 != nil: result.add arg2
     if arg3 != nil: result.add arg3
+    result.typ = sym.typ.sons[0]
 
 # we have 4 cases to consider:
 # - a void proc --> nothing to do
@@ -152,15 +159,21 @@ discard """
 We generate roughly this:
 
 proc f_wrapper(args) =
+  barrierEnter(args.barrier)  # for parallel statement
   var a = args.a # copy strings/seqs; thread transfer; not generated for
                  # the 'parallel' statement
   var b = args.b
 
-  args.fut = createFuture(thread, sizeof(T)) # optional
+  args.fut = nimCreateFuture(thread, sizeof(T)) # optional
+  nimFutureCreateCondVar(args.fut)  # optional
   nimArgsPassingDone() # signal parent that the work is done
+  # 
   args.fut.blob = f(a, b, ...)
+  nimFutureSignal(args.fut)
+  
   # - or -
   f(a, b, ...)
+  barrierLeave(args.barrier)  # for parallel statement
 
 stmtList:
   var scratchObj
@@ -196,8 +209,12 @@ proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
 
   body.add callCodeGenProc("nimArgsPassingDone", threadParam.newSymNode)
   if fut != nil:
-    body.add newAsgnStmt(indirectAccess(fut, 
-        if fut.typ.futureKind==futGC: "data" else: "blob", fut.info), call)
+    let fk = fut.typ.sons[1].futureKind
+    if fk == futInvalid:
+      localError(f.info, "cannot create a future of type: " & 
+        typeToString(fut.typ.sons[1]))
+    body.add newAsgnStmt(indirectAccess(fut,
+      if fk == futGC: "data" else: "blob", fut.info), call)
     if barrier == nil:
       body.add callCodeGenProc("nimFutureSignal", fut)
   else:
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 505c289ea..4e3d2f3ce 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1579,6 +1579,12 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     result = semDirectOp(c, n, flags)
 
+proc createFuture(c: PContext; t: PType; info: TLineInfo): PType =
+  result = newType(tyGenericInvokation, c.module)
+  addSonSkipIntLit(result, magicsys.getCompilerProc("Future").typ)
+  addSonSkipIntLit(result, t)
+  result = instGenericContainer(c, info, result, allowMetaTypes = false)
+
 proc setMs(n: PNode, s: PSym): PNode = 
   result = n
   n.sons[0] = newSymNode(s)
@@ -1610,6 +1616,12 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
     var x = n.lastSon
     if x.kind == nkDo: x = x.sons[bodyPos]
     result.sons[1] = semStmt(c, x)
+  of mSpawn:
+    result = setMs(n, s)
+    result.sons[1] = semExpr(c, n.sons[1])
+    # later passes may transform the type 'Future[T]' back into 'T'
+    if not result[1].typ.isEmptyType:
+      result.typ = createFuture(c, result[1].typ, n.info)
   else: result = semDirectOp(c, n, flags)
 
 proc semWhen(c: PContext, n: PNode, semCheck = true): PNode =
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 3a6bfcf67..f943e7006 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -115,12 +115,6 @@ proc semLocals(c: PContext, n: PNode): PNode =
         if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
         result.add(a)
 
-proc createFuture(c: PContext; t: PType; info: TLineInfo): PType =
-  result = newType(tyGenericInvokation, c.module)
-  addSonSkipIntLit(result, magicsys.getCompilerProc("Future").typ)
-  addSonSkipIntLit(result, t)
-  result = instGenericContainer(c, info, result, allowMetaTypes = false)
-
 proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
 proc magicsAfterOverloadResolution(c: PContext, n: PNode, 
                                    flags: TExprFlags): PNode =
@@ -136,9 +130,4 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   of mShallowCopy: result = semShallowCopy(c, n, flags)
   of mNBindSym: result = semBindSym(c, n)
   of mLocals: result = semLocals(c, n)
-  of mSpawn:
-    result = n
-    # later passes may transform the type 'Future[T]' back into 'T'
-    if not n[1].typ.isEmptyType:
-      result.typ = createFuture(c, n[1].typ, n.info)
   else: result = n
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 583c60c66..41c1adca0 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -57,7 +57,7 @@ proc openBarrier*(b: ptr Barrier) {.compilerProc.} =
   b.cv = createCondVar()
 
 proc closeBarrier*(b: ptr Barrier) {.compilerProc.} =
-  await(b.cv)
+  while b.counter > 0: await(b.cv)
   destroyCondVar(b.cv)
 
 {.pop.}
@@ -136,8 +136,13 @@ proc nimFutureCreateCondVar(fut: RawFuture) {.compilerProc.} =
   fut.usesCondVar = true
 
 proc nimFutureSignal(fut: RawFuture) {.compilerProc.} =
-  assert fut.usesCondVar
-  signal(fut.cv)
+  if fut.ai != nil:
+    acquire(fut.ai.cv.L)
+    fut.ai.idx = fut.idx
+    inc fut.ai.cv.counter
+    release(fut.ai.cv.L)
+    signal(fut.ai.cv.c)
+  if fut.usesCondVar: signal(fut.cv)
 
 proc await*[T](fut: Future[T]) =
   ## waits until the value for the future arrives.
@@ -147,28 +152,21 @@ proc `^`*[T](fut: Future[T]): T =
   ## blocks until the value is available and then returns this value. Note
   ## this reading is destructive for reasons of efficiency and convenience.
   ## This calls ``finished(fut)``.
-  await(fut)
+  if fut.usesCondVar: await(fut)
   when T is string or T is seq or T is ref:
     result = cast[T](fut.data)
   else:
-    result = fut.payload
+    result = fut.blob
   finished(fut)
 
-proc notify*(fut: RawFuture) {.compilerproc.} =
-  if fut.ai != nil:
-    acquire(fut.ai.cv.L)
-    fut.ai.idx = fut.idx
-    inc fut.ai.cv.counter
-    release(fut.ai.cv.L)
-    signal(fut.ai.cv.c)
-  if fut.usesCondVar: signal(fut.cv)
-
 proc awaitAny*(futures: openArray[RawFuture]): int =
   # awaits any of the given futures. Returns the index of one future for which
   ## a value arrived. A future only supports one call to 'awaitAny' at the
   ## same time. That means if you await([a,b]) and await([b,c]) the second
   ## call will only await 'c'. If there is no future left to be able to wait
   ## on, -1 is returned.
+  ## **Note**: This results in non-deterministic behaviour and so should be
+  ## avoided.
   var ai: AwaitInfo
   ai.cv = createCondVar()
   var conflicts = 0
@@ -245,19 +243,18 @@ proc preferSpawn*(): bool =
   ## it is not necessary to call this directly; use 'spawnX' instead.
   result = gSomeReady.counter > 0
 
-proc spawn*(call: stmt) {.magic: "Spawn".}
+proc spawn*(call: expr): expr {.magic: "Spawn".}
   ## always spawns a new task, so that the 'call' is never executed on
   ## the calling thread. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has 'void' as the return type.
 
-template spawnX*(call: stmt) =
+template spawnX*(call: expr): expr =
   ## spawns a new task if a CPU core is ready, otherwise executes the
   ## call in the calling thread. Usually it is advised to
   ## use 'spawn' in order to not block the producer for an unknown
   ## amount of time. 'call' has to be proc call 'p(...)' where 'p'
   ## is gcsafe and has 'void' as the return type.
-  if preferSpawn(): spawn call
-  else: call
+  (if preferSpawn(): spawn call else: call)
 
 proc parallel*(body: stmt) {.magic: "Parallel".}
   ## a parallel section can be used to execute a block in parallel. ``body``
diff --git a/tests/parallel/tflowvar.nim b/tests/parallel/tflowvar.nim
new file mode 100644
index 000000000..77fab14b5
--- /dev/null
+++ b/tests/parallel/tflowvar.nim
@@ -0,0 +1,17 @@
+discard """
+  output: '''foobarfoobarbazbearbazbear'''
+  cmd: "nimrod $target --threads:on $options $file"
+"""
+
+import threadpool
+
+proc computeSomething(a, b: string): string = a & b & a & b
+
+proc main =
+  let fvA = spawn computeSomething("foo", "bar")
+  let fvB = spawn computeSomething("baz", "bear")
+
+  echo(^fvA, ^fvB)
+
+main()
+sync()
diff --git a/tests/system/tsysspawn.nim b/tests/parallel/tsysspawn.nim
index fc7921b0e..fc7921b0e 100644
--- a/tests/system/tsysspawn.nim
+++ b/tests/parallel/tsysspawn.nim
diff --git a/tests/system/tsysspawnbadarg.nim b/tests/parallel/tsysspawnbadarg.nim
index ce3c5611b..120975ed5 100644
--- a/tests/system/tsysspawnbadarg.nim
+++ b/tests/parallel/tsysspawnbadarg.nim
@@ -1,6 +1,6 @@
 discard """
   line: 7
-  errormsg: "'spawn' takes a call expression of type void"
+  errormsg: "'spawn' takes a call expression"
   cmd: "nimrod $target --threads:on $options $file"
 """