summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/ast.nim2
-rw-r--r--compiler/lowerings.nim88
-rw-r--r--compiler/semexprs.nim8
-rw-r--r--compiler/semtypes.nim8
-rw-r--r--lib/pure/concurrency/threadpool.nim134
-rw-r--r--lib/system.nim4
-rw-r--r--lib/system/assign.nim3
7 files changed, 138 insertions, 109 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim
index c47407ee2..c3cb63df4 100644
--- a/compiler/ast.nim
+++ b/compiler/ast.nim
@@ -885,6 +885,8 @@ const
 
   nkCallKinds* = {nkCall, nkInfix, nkPrefix, nkPostfix,
                   nkCommand, nkCallStrLit, nkHiddenCallConv}
+  nkIdentKinds* = {nkIdent, nkSym, nkAccQuoted, nkOpenSymChoice,
+                   nkClosedSymChoice}
 
   nkLiterals* = {nkCharLit..nkTripleStrLit}
   nkLambdaKinds* = {nkLambda, nkDo}
diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim
index 047bdf832..13d4bf60e 100644
--- a/compiler/lowerings.nim
+++ b/compiler/lowerings.nim
@@ -134,26 +134,26 @@ proc callCodegenProc*(name: string, arg1: PNode;
 
 # we have 4 cases to consider:
 # - a void proc --> nothing to do
-# - a proc returning GC'ed memory --> requires a future
+# - a proc returning GC'ed memory --> requires a promise
 # - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
-# - not in a parallel environment --> requires a future for memory safety
+# - not in a parallel environment --> requires a promise for memory safety
 type
   TSpawnResult = enum
-    srVoid, srFuture, srByVar
-  TFutureKind = enum
-    futInvalid # invalid type T for 'Future[T]'
-    futGC      # Future of a GC'ed type
-    futBlob    # Future of a blob type
+    srVoid, srPromise, srByVar
+  TPromiseKind = enum
+    promInvalid # invalid type T for 'Promise[T]'
+    promGC      # Promise of a GC'ed type
+    promBlob    # Promise of a blob type
 
 proc spawnResult(t: PType; inParallel: bool): TSpawnResult =
   if t.isEmptyType: srVoid
   elif inParallel and not containsGarbageCollectedRef(t): srByVar
-  else: srFuture
+  else: srPromise
 
-proc futureKind(t: PType): TFutureKind =
-  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: futGC
-  elif containsGarbageCollectedRef(t): futInvalid
-  else: futBlob
+proc promiseKind(t: PType): TPromiseKind =
+  if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: promGC
+  elif containsGarbageCollectedRef(t): promInvalid
+  else: promBlob
 
 discard """
 We generate roughly this:
@@ -164,12 +164,12 @@ proc f_wrapper(args) =
                  # the 'parallel' statement
   var b = args.b
 
-  args.fut = nimCreateFuture(thread, sizeof(T)) # optional
-  nimFutureCreateCondVar(args.fut)  # optional
+  args.prom = nimCreatePromise(thread, sizeof(T)) # optional
+  nimPromiseCreateCondVar(args.prom)  # optional
   nimArgsPassingDone() # signal parent that the work is done
   # 
-  args.fut.blob = f(a, b, ...)
-  nimFutureSignal(args.fut)
+  args.prom.blob = f(a, b, ...)
+  nimPromiseSignal(args.prom)
   
   # - or -
   f(a, b, ...)
@@ -181,42 +181,42 @@ stmtList:
   scratchObj.b = b
 
   nimSpawn(f_wrapper, addr scratchObj)
-  scratchObj.fut # optional
+  scratchObj.prom # optional
 
 """
 
-proc createNimCreateFutureCall(fut, threadParam: PNode): PNode =
-  let size = newNodeIT(nkCall, fut.info, getSysType(tyInt))
+proc createNimCreatePromiseCall(prom, threadParam: PNode): PNode =
+  let size = newNodeIT(nkCall, prom.info, getSysType(tyInt))
   size.add newSymNode(createMagic("sizeof", mSizeOf))
-  assert fut.typ.kind == tyGenericInst
-  size.add newNodeIT(nkType, fut.info, fut.typ.sons[1])
+  assert prom.typ.kind == tyGenericInst
+  size.add newNodeIT(nkType, prom.info, prom.typ.sons[1])
 
-  let castExpr = newNodeIT(nkCast, fut.info, fut.typ)
+  let castExpr = newNodeIT(nkCast, prom.info, prom.typ)
   castExpr.add emptyNode
-  castExpr.add callCodeGenProc("nimCreateFuture", threadParam, size)
-  result = newFastAsgnStmt(fut, castExpr)
+  castExpr.add callCodeGenProc("nimCreatePromise", threadParam, size)
+  result = newFastAsgnStmt(prom, castExpr)
 
 proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
-                       varSection, call, barrier, fut: PNode): PSym =
+                       varSection, call, barrier, prom: PNode): PSym =
   var body = newNodeI(nkStmtList, f.info)
   body.add varSection
   if barrier != nil:
     body.add callCodeGenProc("barrierEnter", barrier)
-  if fut != nil:
-    body.add createNimCreateFutureCall(fut, threadParam.newSymNode)
+  if prom != nil:
+    body.add createNimCreatePromiseCall(prom, threadParam.newSymNode)
     if barrier == nil:
-      body.add callCodeGenProc("nimFutureCreateCondVar", fut)
+      body.add callCodeGenProc("nimPromiseCreateCondVar", prom)
 
   body.add callCodeGenProc("nimArgsPassingDone", threadParam.newSymNode)
-  if fut != nil:
-    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 prom != nil:
+    let fk = prom.typ.sons[1].promiseKind
+    if fk == promInvalid:
+      localError(f.info, "cannot create a promise of type: " & 
+        typeToString(prom.typ.sons[1]))
+    body.add newAsgnStmt(indirectAccess(prom,
+      if fk == promGC: "data" else: "blob", prom.info), call)
     if barrier == nil:
-      body.add callCodeGenProc("nimFutureSignal", fut)
+      body.add callCodeGenProc("nimPromiseSignal", prom)
   else:
     body.add call
   if barrier != nil:
@@ -381,7 +381,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode; retType: PType;
   of srVoid:
     internalAssert dest == nil
     result = newNodeI(nkStmtList, n.info)
-  of srFuture:
+  of srPromise:
     internalAssert dest == nil
     result = newNodeIT(nkStmtListExpr, n.info, retType)
   of srByVar:
@@ -450,17 +450,17 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode; retType: PType;
     result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
     barrierAsExpr = indirectAccess(castExpr, field, n.info)
 
-  var futField, futAsExpr: PNode = nil
-  if spawnKind == srFuture:
-    var field = newSym(skField, getIdent"fut", owner, n.info)
+  var promField, promAsExpr: PNode = nil
+  if spawnKind == srPromise:
+    var field = newSym(skField, getIdent"prom", owner, n.info)
     field.typ = retType
     objType.addField(field)
-    futField = newDotExpr(scratchObj, field)
-    futAsExpr = indirectAccess(castExpr, field, n.info)
+    promField = newDotExpr(scratchObj, field)
+    promAsExpr = indirectAccess(castExpr, field, n.info)
 
   let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call,
-                                  barrierAsExpr, futAsExpr)
+                                  barrierAsExpr, promAsExpr)
   result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
                              genAddrOf(scratchObj.newSymNode))
 
-  if spawnKind == srFuture: result.add futField
+  if spawnKind == srPromise: result.add promField
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index 4e3d2f3ce..8f4cce547 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -1579,9 +1579,9 @@ proc semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode =
   else:
     result = semDirectOp(c, n, flags)
 
-proc createFuture(c: PContext; t: PType; info: TLineInfo): PType =
+proc createPromise(c: PContext; t: PType; info: TLineInfo): PType =
   result = newType(tyGenericInvokation, c.module)
-  addSonSkipIntLit(result, magicsys.getCompilerProc("Future").typ)
+  addSonSkipIntLit(result, magicsys.getCompilerProc("Promise").typ)
   addSonSkipIntLit(result, t)
   result = instGenericContainer(c, info, result, allowMetaTypes = false)
 
@@ -1619,9 +1619,9 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   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'
+    # later passes may transform the type 'Promise[T]' back into 'T'
     if not result[1].typ.isEmptyType:
-      result.typ = createFuture(c, result[1].typ, n.info)
+      result.typ = createPromise(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/semtypes.nim b/compiler/semtypes.nim
index 8fcb6ea99..bb81cbe74 100644
--- a/compiler/semtypes.nim
+++ b/compiler/semtypes.nim
@@ -1084,8 +1084,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
   of nkCallKinds:
     if isRange(n):
       result = semRangeAux(c, n, prev)
-    elif n[0].kind == nkIdent:
-      let op = n.sons[0].ident
+    elif n[0].kind notin nkIdentKinds:
+      result = semTypeExpr(c, n)
+    else:
+      let op = considerAcc(n.sons[0])
       if op.id in {ord(wAnd), ord(wOr)} or op.s == "|":
         checkSonsLen(n, 3)
         var
@@ -1120,8 +1122,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
         result = semAnyRef(c, n, tyRef, prev)
       else:
         result = semTypeExpr(c, n)
-    else:
-      result = semTypeExpr(c, n)
   of nkWhenStmt:
     var whenResult = semWhen(c, n, false)
     if whenResult.kind == nkStmtList: whenResult.kind = nkStmtListType
diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim
index 41c1adca0..24cb9ccdd 100644
--- a/lib/pure/concurrency/threadpool.nim
+++ b/lib/pure/concurrency/threadpool.nim
@@ -65,12 +65,14 @@ proc closeBarrier*(b: ptr Barrier) {.compilerProc.} =
 # ----------------------------------------------------------------------------
 
 type
+  foreign* = object ## a region that indicates the pointer comes from a
+                    ## foreign thread heap.
   AwaitInfo = object
     cv: CondVar
     idx: int
 
-  RawFuture* = ptr RawFutureObj ## untyped base class for 'Future[T]'
-  RawFutureObj {.inheritable.} = object # \
+  RawPromise* = ptr RawPromiseObj ## untyped base class for 'Promise[T]'
+  RawPromiseObj {.inheritable.} = object # \
     # we allocate this with the thread local allocator; this
     # is possible since we already need to do the GC_unref
     # on the owning thread
@@ -81,10 +83,10 @@ type
     idx: int
     data: PObject  # we incRef and unref it to keep it alive
     owner: ptr Worker
-    next: RawFuture
+    next: RawPromise
     align: float64 # a float for proper alignment
 
-  Future* {.compilerProc.} [T] = ptr object of RawFutureObj
+  Promise* {.compilerProc.} [T] = ptr object of RawPromiseObj
     blob: T  ## the underlying value, if available. Note that usually
              ## you should not access this field directly! However it can
              ## sometimes be more efficient than getting the value via ``^``.
@@ -99,24 +101,24 @@ type
     ready: bool # put it here for correct alignment!
     initialized: bool # whether it has even been initialized
     shutdown: bool # the pool requests to shut down this worker thread
-    futureLock: TLock
-    head: RawFuture
+    promiseLock: TLock
+    head: RawPromise
 
-proc finished*(fut: RawFuture) =
-  ## This MUST be called for every created future to free its associated
+proc finished*(prom: RawPromise) =
+  ## This MUST be called for every created promise to free its associated
   ## resources. Note that the default reading operation ``^`` is destructive
   ## and calls ``finished``.
-  doAssert fut.ai.isNil, "future is still attached to an 'awaitAny'"
-  assert fut.next == nil
-  let w = fut.owner
-  acquire(w.futureLock)
-  fut.next = w.head
-  w.head = fut
-  release(w.futureLock)
-
-proc cleanFutures(w: ptr Worker) =
+  doAssert prom.ai.isNil, "promise is still attached to an 'awaitAny'"
+  assert prom.next == nil
+  let w = prom.owner
+  acquire(w.promiseLock)
+  prom.next = w.head
+  w.head = prom
+  release(w.promiseLock)
+
+proc cleanPromises(w: ptr Worker) =
   var it = w.head
-  acquire(w.futureLock)
+  acquire(w.promiseLock)
   while it != nil:
     let nxt = it.next
     if it.usesCondVar: destroyCondVar(it.cv)
@@ -124,62 +126,84 @@ proc cleanFutures(w: ptr Worker) =
     dealloc(it)
     it = nxt
   w.head = nil
-  release(w.futureLock)
+  release(w.promiseLock)
 
-proc nimCreateFuture(owner: pointer; blobSize: int): RawFuture {.
+proc nimCreatePromise(owner: pointer; blobSize: int): RawPromise {.
                      compilerProc.} =
-  result = cast[RawFuture](alloc0(RawFutureObj.sizeof + blobSize))
+  result = cast[RawPromise](alloc0(RawPromiseObj.sizeof + blobSize))
   result.owner = cast[ptr Worker](owner)
 
-proc nimFutureCreateCondVar(fut: RawFuture) {.compilerProc.} =
-  fut.cv = createCondVar()
-  fut.usesCondVar = true
-
-proc nimFutureSignal(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 nimPromiseCreateCondVar(prom: RawPromise) {.compilerProc.} =
+  prom.cv = createCondVar()
+  prom.usesCondVar = true
+
+proc nimPromiseSignal(prom: RawPromise) {.compilerProc.} =
+  if prom.ai != nil:
+    acquire(prom.ai.cv.L)
+    prom.ai.idx = prom.idx
+    inc prom.ai.cv.counter
+    release(prom.ai.cv.L)
+    signal(prom.ai.cv.c)
+  if prom.usesCondVar: signal(prom.cv)
+
+proc await*[T](prom: Promise[T]) =
+  ## waits until the value for the promise arrives.
+  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
+  ## 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 ``^``.
+  if prom.usesCondVar: await(prom)
+  when T is string or T is seq:
+    action(cast[T](prom.data))
+  elif T is ref:
+    {.error: "'awaitAndThen' not available for Promise[ref]".}
+  else:
+    action(prom.blob)
+  finished(prom)
 
-proc await*[T](fut: Future[T]) =
-  ## waits until the value for the future arrives.
-  if fut.usesCondVar: await(fut.cv)
+proc `^`*[T](prom: Promise[ref T]): foreign ptr 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(prom)``.
+  if prom.usesCondVar: await(prom)
+  result = cast[foreign ptr T](prom.data)
+  finished(prom)
 
-proc `^`*[T](fut: Future[T]): T =
+proc `^`*[T](prom: Promise[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)``.
-  if fut.usesCondVar: await(fut)
-  when T is string or T is seq or T is ref:
-    result = cast[T](fut.data)
+  ## This calls ``finished(prom)``.
+  if prom.usesCondVar: await(prom)
+  when T is string or T is seq:
+    result = cast[T](prom.data)
   else:
-    result = fut.blob
-  finished(fut)
+    result = prom.blob
+  finished(prom)
 
-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
+proc awaitAny*(promises: openArray[RawPromise]): int =
+  # awaits any of the given promises. Returns the index of one promise for which
+  ## a value arrived. A promise 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
+  ## call will only await 'c'. If there is no promise 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
-  for i in 0 .. futures.high:
-    if cas(addr futures[i].ai, nil, addr ai):
-      futures[i].idx = i
+  for i in 0 .. promises.high:
+    if cas(addr promises[i].ai, nil, addr ai):
+      promises[i].idx = i
     else:
       inc conflicts
-  if conflicts < futures.len:
+  if conflicts < promises.len:
     await(ai.cv)
     result = ai.idx
-    for i in 0 .. futures.high:
-      discard cas(addr futures[i].ai, addr ai, nil)
+    for i in 0 .. promises.high:
+      discard cas(addr promises[i].ai, addr ai, nil)
   else:
     result = -1
   destroyCondVar(ai.cv)
@@ -207,7 +231,7 @@ proc slave(w: ptr Worker) {.thread.} =
     await(w.taskArrived)
     assert(not w.ready)
     w.f(w, w.data)
-    if w.head != nil: w.cleanFutures
+    if w.head != nil: w.cleanPromises
     if w.shutdown:
       w.shutdown = false
       atomicDec currentPoolSize
@@ -228,7 +252,7 @@ var
 proc activateThread(i: int) {.noinline.} =
   workersData[i].taskArrived = createCondVar()
   workersData[i].taskStarted = createCondVar()
-  initLock workersData[i].futureLock
+  initLock workersData[i].promiseLock
   workersData[i].initialized = true
   createThread(workers[i], slave, addr(workersData[i]))
 
diff --git a/lib/system.nim b/lib/system.nim
index fbd905afa..fc6f617a5 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -42,7 +42,6 @@ type
   cstring* {.magic: Cstring.} ## built-in cstring (*compatible string*) type
   pointer* {.magic: Pointer.} ## built-in pointer type, use the ``addr``
                               ## operator to get a pointer to a variable
-
 const
   on* = true    ## alias for ``true``
   off* = false  ## alias for ``false``
@@ -51,6 +50,9 @@ const
 
 type
   Ordinal* {.magic: Ordinal.}[T]
+  `ptr`* {.magic: Pointer.}[T] ## built-in generic untraced pointer type
+  `ref`* {.magic: Pointer.}[T] ## built-in generic traced pointer type
+
   `nil` {.magic: "Nil".}
   expr* {.magic: Expr.} ## meta type to denote an expression (for templates)
   stmt* {.magic: Stmt.} ## meta type to denote a statement (for templates)
diff --git a/lib/system/assign.nim b/lib/system/assign.nim
index 75c749633..2ae945fb1 100644
--- a/lib/system/assign.nim
+++ b/lib/system/assign.nim
@@ -179,7 +179,8 @@ when not defined(nimmixin):
     # internal proc used for destroying sequences and arrays
     for i in countup(0, r.len - 1): destroy(r[i])
 else:
-  # XXX Why is this exported and no compilerproc?
+  # XXX Why is this exported and no compilerproc? -> compilerprocs cannot be
+  # generic for now
   proc nimDestroyRange*[T](r: T) =
     # internal proc used for destroying sequences and arrays
     mixin destroy