'parallel' statement almost working
This commit is contained in:
parent
31b8fd66b1
commit
417b9f5a1d
10 changed files with 470 additions and 115 deletions
|
|
@ -1636,7 +1636,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) =
|
||||||
of mSlurp..mQuoteAst:
|
of mSlurp..mQuoteAst:
|
||||||
localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
|
localError(e.info, errXMustBeCompileTime, e.sons[0].sym.name.s)
|
||||||
of mSpawn:
|
of mSpawn:
|
||||||
let n = lowerings.wrapProcForSpawn(p.module.module, e.sons[1])
|
let n = lowerings.wrapProcForSpawn(p.module.module, e[1], e.typ, nil, nil)
|
||||||
expr(p, n, d)
|
expr(p, n, d)
|
||||||
of mParallel:
|
of mParallel:
|
||||||
let n = semparallel.liftParallel(p.module.module, e)
|
let n = semparallel.liftParallel(p.module.module, e)
|
||||||
|
|
|
||||||
|
|
@ -672,12 +672,8 @@ proc simpleSlice*(a, b: PNode): BiggestInt =
|
||||||
else:
|
else:
|
||||||
result = -1
|
result = -1
|
||||||
|
|
||||||
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
proc ple(m: TModel; a, b: PNode): TImplication =
|
||||||
let res = canon(opLe.buildCall(a, b))
|
template `<=?`(a,b): expr = ple(m,a,b) == impYes
|
||||||
#echo renderTree(res)
|
|
||||||
# we hardcode lots of axioms here:
|
|
||||||
let a = res[1]
|
|
||||||
let b = res[2]
|
|
||||||
# 0 <= 3
|
# 0 <= 3
|
||||||
if a.isValue and b.isValue:
|
if a.isValue and b.isValue:
|
||||||
return if leValue(a, b): impYes else: impNo
|
return if leValue(a, b): impYes else: impNo
|
||||||
|
|
@ -692,26 +688,46 @@ proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
||||||
# x <= x
|
# x <= x
|
||||||
if sameTree(a, b): return impYes
|
if sameTree(a, b): return impYes
|
||||||
|
|
||||||
# x <= x+c iff 0 <= c
|
# 0 <= x.len
|
||||||
if b.getMagic in someAdd and sameTree(a, b[1]):
|
if b.getMagic in someLen and a.isValue:
|
||||||
return proveLe(m, zero(), b[2])
|
if a.intVal <= 0: return impYes
|
||||||
|
|
||||||
# x+c <= x iff c <= 0
|
# x <= y+c if 0 <= c and x <= y
|
||||||
if a.getMagic in someAdd and sameTree(b, a[1]):
|
if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes
|
||||||
return proveLe(m, a[2], zero())
|
|
||||||
|
|
||||||
# x <= x*c if 1 <= c and 0 <= x:
|
# x+c <= y if c <= 0 and x <= y
|
||||||
if b.getMagic in someMul and sameTree(a, b[1]):
|
if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes
|
||||||
if proveLe(m, one(), b[2]) == impYes and proveLe(m, zero(), a) == impYes:
|
|
||||||
return impYes
|
|
||||||
|
|
||||||
# x div c <= x if 1 <= c and 0 <= x:
|
# x <= y*c if 1 <= c and x <= y and 0 <= y
|
||||||
if a.getMagic in someDiv and sameTree(a[1], b):
|
if b.getMagic in someMul:
|
||||||
if proveLe(m, one(), a[2]) == impYes and proveLe(m, zero(), b) == impYes:
|
if a <=? b[1] and one() <=? b[2] and zero() <=? b[1]: return impYes
|
||||||
return impYes
|
|
||||||
|
# x div c <= y if 1 <= c and 0 <= y and x <= y:
|
||||||
|
if a.getMagic in someDiv:
|
||||||
|
if one() <=? a[2] and zero() <=? b and a[1] <=? b: return impYes
|
||||||
|
|
||||||
|
# slightly subtle:
|
||||||
|
# x <= max(y, z) iff x <= y or x <= z
|
||||||
|
# note that 'x <= max(x, z)' is a special case of the above rule
|
||||||
|
if b.getMagic in someMax:
|
||||||
|
if a <=? b[1] or a <=? b[2]: return impYes
|
||||||
|
|
||||||
|
# min(x, y) <= z iff x <= z or y <= z
|
||||||
|
if a.getMagic in someMin:
|
||||||
|
if a[1] <=? b or a[2] <=? b: return impYes
|
||||||
|
|
||||||
# use the knowledge base:
|
# use the knowledge base:
|
||||||
return doesImply(m, res)
|
return doesImply(m, opLe.buildCall(a, b))
|
||||||
|
|
||||||
|
proc proveLe*(m: TModel; a, b: PNode): TImplication =
|
||||||
|
#echo "ROOT ", renderTree(a), " <=? ", b.rendertree
|
||||||
|
let x = canon(opLe.buildCall(a, b))
|
||||||
|
#echo renderTree(res)
|
||||||
|
result = ple(m, x[1], x[2])
|
||||||
|
if result == impUnknown:
|
||||||
|
# try an alternative: a <= b iff not (b < a) iff not (b+1 <= a):
|
||||||
|
let y = canon(opLe.buildCall(opAdd.buildCall(b, one()), a))
|
||||||
|
result = ~ple(m, y[1], y[2])
|
||||||
|
|
||||||
proc addFactLe*(m: var TModel; a, b: PNode) =
|
proc addFactLe*(m: var TModel; a, b: PNode) =
|
||||||
m.add canon(opLe.buildCall(a, b))
|
m.add canon(opLe.buildCall(a, b))
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ const
|
||||||
genPrefix* = ":tmp" # prefix for generated names
|
genPrefix* = ":tmp" # prefix for generated names
|
||||||
|
|
||||||
import ast, astalgo, types, idents, magicsys, msgs, options
|
import ast, astalgo, types, idents, magicsys, msgs, options
|
||||||
|
from guards import createMagic
|
||||||
|
from trees import getMagic
|
||||||
|
|
||||||
proc newTupleAccess*(tup: PNode, i: int): PNode =
|
proc newTupleAccess*(tup: PNode, i: int): PNode =
|
||||||
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
result = newNodeIT(nkBracketExpr, tup.info, tup.typ.skipTypes(
|
||||||
|
|
@ -80,19 +82,23 @@ proc newDotExpr(obj, b: PSym): PNode =
|
||||||
addSon(result, newSymNode(field))
|
addSon(result, newSymNode(field))
|
||||||
result.typ = field.typ
|
result.typ = field.typ
|
||||||
|
|
||||||
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
|
proc indirectAccess*(a: PNode, b: string, info: TLineInfo): PNode =
|
||||||
# returns a[].b as a node
|
# returns a[].b as a node
|
||||||
var deref = newNodeI(nkHiddenDeref, info)
|
var deref = newNodeI(nkHiddenDeref, info)
|
||||||
deref.typ = a.typ.sons[0]
|
deref.typ = a.typ.skipTypes(abstractInst).sons[0]
|
||||||
assert deref.typ.kind == tyObject
|
assert deref.typ.kind == tyObject
|
||||||
let field = getSymFromList(deref.typ.n, getIdent(b.name.s & $b.id))
|
let field = getSymFromList(deref.typ.n, getIdent(b))
|
||||||
assert field != nil, b.name.s
|
assert field != nil, b
|
||||||
addSon(deref, a)
|
addSon(deref, a)
|
||||||
result = newNodeI(nkDotExpr, info)
|
result = newNodeI(nkDotExpr, info)
|
||||||
addSon(result, deref)
|
addSon(result, deref)
|
||||||
addSon(result, newSymNode(field))
|
addSon(result, newSymNode(field))
|
||||||
result.typ = field.typ
|
result.typ = field.typ
|
||||||
|
|
||||||
|
proc indirectAccess*(a: PNode, b: PSym, info: TLineInfo): PNode =
|
||||||
|
# returns a[].b as a node
|
||||||
|
result = indirectAccess(a, b.name.s & $b.id, info)
|
||||||
|
|
||||||
proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode =
|
||||||
result = indirectAccess(newSymNode(a), b, info)
|
result = indirectAccess(newSymNode(a), b, info)
|
||||||
|
|
||||||
|
|
@ -102,6 +108,11 @@ proc genAddrOf*(n: PNode): PNode =
|
||||||
result.typ = newType(tyPtr, n.typ.owner)
|
result.typ = newType(tyPtr, n.typ.owner)
|
||||||
result.typ.rawAddSon(n.typ)
|
result.typ.rawAddSon(n.typ)
|
||||||
|
|
||||||
|
proc genDeref*(n: PNode): PNode =
|
||||||
|
result = newNodeIT(nkHiddenDeref, n.info,
|
||||||
|
n.typ.skipTypes(abstractInst).sons[0])
|
||||||
|
result.add n
|
||||||
|
|
||||||
proc callCodegenProc*(name: string, arg1: PNode;
|
proc callCodegenProc*(name: string, arg1: PNode;
|
||||||
arg2, arg3: PNode = nil): PNode =
|
arg2, arg3: PNode = nil): PNode =
|
||||||
result = newNodeI(nkCall, arg1.info)
|
result = newNodeI(nkCall, arg1.info)
|
||||||
|
|
@ -114,14 +125,83 @@ proc callCodegenProc*(name: string, arg1: PNode;
|
||||||
if arg2 != nil: result.add arg2
|
if arg2 != nil: result.add arg2
|
||||||
if arg3 != nil: result.add arg3
|
if arg3 != nil: result.add arg3
|
||||||
|
|
||||||
|
# we have 4 cases to consider:
|
||||||
|
# - a void proc --> nothing to do
|
||||||
|
# - a proc returning GC'ed memory --> requires a future
|
||||||
|
# - a proc returning non GC'ed memory --> pass as hidden 'var' parameter
|
||||||
|
# - not in a parallel environment --> requires a future 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
|
||||||
|
|
||||||
|
proc spawnResult(t: PType; inParallel: bool): TSpawnResult =
|
||||||
|
if t.isEmptyType: srVoid
|
||||||
|
elif inParallel and not containsGarbageCollectedRef(t): srByVar
|
||||||
|
else: srFuture
|
||||||
|
|
||||||
|
proc futureKind(t: PType): TFutureKind =
|
||||||
|
if t.skipTypes(abstractInst).kind in {tyRef, tyString, tySequence}: futGC
|
||||||
|
elif containsGarbageCollectedRef(t): futInvalid
|
||||||
|
else: futBlob
|
||||||
|
|
||||||
|
discard """
|
||||||
|
We generate roughly this:
|
||||||
|
|
||||||
|
proc f_wrapper(args) =
|
||||||
|
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
|
||||||
|
nimArgsPassingDone() # signal parent that the work is done
|
||||||
|
args.fut.blob = f(a, b, ...)
|
||||||
|
# - or -
|
||||||
|
f(a, b, ...)
|
||||||
|
|
||||||
|
stmtList:
|
||||||
|
var scratchObj
|
||||||
|
scratchObj.a = a
|
||||||
|
scratchObj.b = b
|
||||||
|
|
||||||
|
nimSpawn(f_wrapper, addr scratchObj)
|
||||||
|
scratchObj.fut # optional
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
proc createNimCreateFutureCall(fut, threadParam: PNode): PNode =
|
||||||
|
let size = newNodeIT(nkCall, fut.info, getSysType(tyInt))
|
||||||
|
size.add newSymNode(createMagic("sizeof", mSizeOf))
|
||||||
|
assert fut.typ.kind == tyGenericInst
|
||||||
|
size.add newNodeIT(nkType, fut.info, fut.typ.sons[1])
|
||||||
|
|
||||||
|
let castExpr = newNodeIT(nkCast, fut.info, fut.typ)
|
||||||
|
castExpr.add emptyNode
|
||||||
|
castExpr.add callCodeGenProc("nimCreateFuture", threadParam, size)
|
||||||
|
result = newFastAsgnStmt(fut, castExpr)
|
||||||
|
|
||||||
proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
|
proc createWrapperProc(f: PNode; threadParam, argsParam: PSym;
|
||||||
varSection, call, barrier: PNode): PSym =
|
varSection, call, barrier, fut: PNode): PSym =
|
||||||
var body = newNodeI(nkStmtList, f.info)
|
var body = newNodeI(nkStmtList, f.info)
|
||||||
body.add varSection
|
body.add varSection
|
||||||
if barrier != nil:
|
if barrier != nil:
|
||||||
body.add callCodeGenProc("barrierEnter", barrier)
|
body.add callCodeGenProc("barrierEnter", barrier)
|
||||||
body.add callCodeGenProc("nimArgsPassingDone", newSymNode(threadParam))
|
if fut != nil:
|
||||||
body.add call
|
body.add createNimCreateFutureCall(fut, threadParam.newSymNode)
|
||||||
|
if barrier == nil:
|
||||||
|
body.add callCodeGenProc("nimFutureCreateCondVar", fut)
|
||||||
|
|
||||||
|
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)
|
||||||
|
if barrier == nil:
|
||||||
|
body.add callCodeGenProc("nimFutureSignal", fut)
|
||||||
|
else:
|
||||||
|
body.add call
|
||||||
if barrier != nil:
|
if barrier != nil:
|
||||||
body.add callCodeGenProc("barrierLeave", barrier)
|
body.add callCodeGenProc("barrierLeave", barrier)
|
||||||
|
|
||||||
|
|
@ -151,10 +231,148 @@ proc createCastExpr(argsParam: PSym; objType: PType): PNode =
|
||||||
result.typ = newType(tyPtr, objType.owner)
|
result.typ = newType(tyPtr, objType.owner)
|
||||||
result.typ.rawAddSon(objType)
|
result.typ.rawAddSon(objType)
|
||||||
|
|
||||||
proc wrapProcForSpawn*(owner: PSym; n: PNode; barrier: PNode = nil): PNode =
|
proc setupArgsForConcurrency(n: PNode; objType: PType; scratchObj: PSym,
|
||||||
result = newNodeI(nkStmtList, n.info)
|
castExpr, call, varSection, result: PNode) =
|
||||||
if n.kind notin nkCallKinds or not n.typ.isEmptyType:
|
let formals = n[0].typ.n
|
||||||
localError(n.info, "'spawn' takes a call expression of type void")
|
let tmpName = getIdent(genPrefix)
|
||||||
|
for i in 1 .. <n.len:
|
||||||
|
# we pick n's type here, which hopefully is 'tyArray' and not
|
||||||
|
# 'tyOpenArray':
|
||||||
|
var argType = n[i].typ.skipTypes(abstractInst)
|
||||||
|
if i < formals.len and formals[i].typ.kind == tyVar:
|
||||||
|
localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
|
||||||
|
elif containsTyRef(argType):
|
||||||
|
localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
|
||||||
|
|
||||||
|
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
||||||
|
var field = newSym(skField, fieldname, objType.owner, n.info)
|
||||||
|
field.typ = argType
|
||||||
|
objType.addField(field)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
|
||||||
|
|
||||||
|
var temp = newSym(skTemp, tmpName, objType.owner, n.info)
|
||||||
|
temp.typ = argType
|
||||||
|
incl(temp.flags, sfFromGeneric)
|
||||||
|
|
||||||
|
var vpart = newNodeI(nkIdentDefs, n.info, 3)
|
||||||
|
vpart.sons[0] = newSymNode(temp)
|
||||||
|
vpart.sons[1] = ast.emptyNode
|
||||||
|
vpart.sons[2] = indirectAccess(castExpr, field, n.info)
|
||||||
|
varSection.add vpart
|
||||||
|
|
||||||
|
call.add(newSymNode(temp))
|
||||||
|
|
||||||
|
proc getRoot*(n: PNode): PSym =
|
||||||
|
## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
|
||||||
|
## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
|
||||||
|
## determined as the owner; ``obj`` in the example.
|
||||||
|
case n.kind
|
||||||
|
of nkSym:
|
||||||
|
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
|
||||||
|
result = n.sym
|
||||||
|
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
|
||||||
|
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
||||||
|
result = getRoot(n.sons[0])
|
||||||
|
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
||||||
|
result = getRoot(n.sons[1])
|
||||||
|
of nkCallKinds:
|
||||||
|
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
|
||||||
|
else: discard
|
||||||
|
|
||||||
|
proc newIntLit(value: BiggestInt): PNode =
|
||||||
|
result = nkIntLit.newIntNode(value)
|
||||||
|
result.typ = getSysType(tyInt)
|
||||||
|
|
||||||
|
proc genHigh(n: PNode): PNode =
|
||||||
|
if skipTypes(n.typ, abstractVar).kind in {tyArrayConstr, tyArray}:
|
||||||
|
result = newIntLit(lastOrd(skipTypes(n.typ, abstractVar)))
|
||||||
|
else:
|
||||||
|
result = newNodeI(nkCall, n.info, 2)
|
||||||
|
result.typ = getSysType(tyInt)
|
||||||
|
result.sons[0] = newSymNode(createMagic("high", mHigh))
|
||||||
|
result.sons[1] = n
|
||||||
|
|
||||||
|
proc setupArgsForParallelism(n: PNode; objType: PType; scratchObj: PSym;
|
||||||
|
castExpr, call, result: PNode) =
|
||||||
|
let formals = n[0].typ.n
|
||||||
|
let tmpName = getIdent(genPrefix)
|
||||||
|
for i in 1 .. <n.len:
|
||||||
|
let n = n[i]
|
||||||
|
let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ,
|
||||||
|
abstractInst)
|
||||||
|
if containsTyRef(argType):
|
||||||
|
localError(n.info, "'spawn'ed function cannot refer to 'ref'/closure")
|
||||||
|
|
||||||
|
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
||||||
|
var field = newSym(skField, fieldname, objType.owner, n.info)
|
||||||
|
|
||||||
|
if argType.kind in {tyVarargs, tyOpenArray}:
|
||||||
|
# important special case: we always create a zero-copy slice:
|
||||||
|
let slice = newNodeI(nkCall, n.info, 4)
|
||||||
|
slice.typ = n.typ
|
||||||
|
slice.sons[0] = newSymNode(createMagic("slice", mSlice))
|
||||||
|
var fieldB = newSym(skField, tmpName, objType.owner, n.info)
|
||||||
|
fieldB.typ = getSysType(tyInt)
|
||||||
|
objType.addField(fieldB)
|
||||||
|
|
||||||
|
if getMagic(n) == mSlice:
|
||||||
|
let a = genAddrOf(n[0])
|
||||||
|
field.typ = a.typ
|
||||||
|
objType.addField(field)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||||
|
|
||||||
|
var fieldA = newSym(skField, tmpName, objType.owner, n.info)
|
||||||
|
fieldA.typ = getSysType(tyInt)
|
||||||
|
objType.addField(fieldA)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2])
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3])
|
||||||
|
|
||||||
|
slice.sons[2] = indirectAccess(castExpr, fieldA, n.info)
|
||||||
|
else:
|
||||||
|
let a = genAddrOf(n)
|
||||||
|
field.typ = a.typ
|
||||||
|
objType.addField(field)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(n))
|
||||||
|
|
||||||
|
slice.sons[2] = newIntLit(0)
|
||||||
|
|
||||||
|
slice.sons[1] = genDeref(indirectAccess(castExpr, field, n.info))
|
||||||
|
slice.sons[3] = indirectAccess(castExpr, fieldB, n.info)
|
||||||
|
call.add slice
|
||||||
|
elif (let size = computeSize(argType); size < 0 or size > 16) and
|
||||||
|
n.getRoot != nil:
|
||||||
|
# it is more efficient to pass a pointer instead:
|
||||||
|
let a = genAddrOf(n)
|
||||||
|
field.typ = a.typ
|
||||||
|
objType.addField(field)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a)
|
||||||
|
call.add(genDeref(indirectAccess(castExpr, field, n.info)))
|
||||||
|
else:
|
||||||
|
# boring case
|
||||||
|
field.typ = argType
|
||||||
|
objType.addField(field)
|
||||||
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n)
|
||||||
|
call.add(indirectAccess(castExpr, field, n.info))
|
||||||
|
|
||||||
|
proc wrapProcForSpawn*(owner: PSym; n: PNode; retType: PType;
|
||||||
|
barrier, dest: PNode = nil): PNode =
|
||||||
|
# if 'barrier' != nil, then it is in a 'parallel' section and we
|
||||||
|
# generate quite different code
|
||||||
|
let spawnKind = spawnResult(retType, barrier!=nil)
|
||||||
|
case spawnKind
|
||||||
|
of srVoid:
|
||||||
|
internalAssert dest == nil
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
of srFuture:
|
||||||
|
internalAssert dest == nil
|
||||||
|
result = newNodeIT(nkStmtListExpr, n.info, retType)
|
||||||
|
of srByVar:
|
||||||
|
if dest == nil: localError(n.info, "'spawn' must not be discarded")
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
|
||||||
|
if n.kind notin nkCallKinds:
|
||||||
|
localError(n.info, "'spawn' takes a call expression")
|
||||||
return
|
return
|
||||||
if optThreadAnalysis in gGlobalOptions:
|
if optThreadAnalysis in gGlobalOptions:
|
||||||
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
||||||
|
|
@ -180,7 +398,7 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode; barrier: PNode = nil): PNode =
|
||||||
varSectionB.addVar(scratchObj.newSymNode)
|
varSectionB.addVar(scratchObj.newSymNode)
|
||||||
result.add varSectionB
|
result.add varSectionB
|
||||||
|
|
||||||
var call = newNodeI(nkCall, n.info)
|
var call = newNodeIT(nkCall, n.info, n.typ)
|
||||||
var fn = n.sons[0]
|
var fn = n.sons[0]
|
||||||
# templates and macros are in fact valid here due to the nature of
|
# templates and macros are in fact valid here due to the nature of
|
||||||
# the transformation:
|
# the transformation:
|
||||||
|
|
@ -200,34 +418,10 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode; barrier: PNode = nil): PNode =
|
||||||
|
|
||||||
call.add(fn)
|
call.add(fn)
|
||||||
var varSection = newNodeI(nkVarSection, n.info)
|
var varSection = newNodeI(nkVarSection, n.info)
|
||||||
let formals = n[0].typ.n
|
if barrier.isNil:
|
||||||
let tmpName = getIdent(genPrefix)
|
setupArgsForConcurrency(n, objType, scratchObj, castExpr, call, varSection, result)
|
||||||
for i in 1 .. <n.len:
|
else:
|
||||||
# we pick n's type here, which hopefully is 'tyArray' and not
|
setupArgsForParallelism(n, objType, scratchObj, castExpr, call, result)
|
||||||
# 'tyOpenArray':
|
|
||||||
var argType = n[i].typ.skipTypes(abstractInst)
|
|
||||||
if i < formals.len and formals[i].typ.kind == tyVar:
|
|
||||||
localError(n[i].info, "'spawn'ed function cannot have a 'var' parameter")
|
|
||||||
elif containsTyRef(argType):
|
|
||||||
localError(n[i].info, "'spawn'ed function cannot refer to 'ref'/closure")
|
|
||||||
|
|
||||||
let fieldname = if i < formals.len: formals[i].sym.name else: tmpName
|
|
||||||
var field = newSym(skField, fieldname, owner, n.info)
|
|
||||||
field.typ = argType
|
|
||||||
objType.addField(field)
|
|
||||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i])
|
|
||||||
|
|
||||||
var temp = newSym(skTemp, tmpName, owner, n.info)
|
|
||||||
temp.typ = argType
|
|
||||||
incl(temp.flags, sfFromGeneric)
|
|
||||||
|
|
||||||
var vpart = newNodeI(nkIdentDefs, n.info, 3)
|
|
||||||
vpart.sons[0] = newSymNode(temp)
|
|
||||||
vpart.sons[1] = ast.emptyNode
|
|
||||||
vpart.sons[2] = indirectAccess(castExpr, field, n.info)
|
|
||||||
varSection.add vpart
|
|
||||||
|
|
||||||
call.add(newSymNode(temp))
|
|
||||||
|
|
||||||
var barrierAsExpr: PNode = nil
|
var barrierAsExpr: PNode = nil
|
||||||
if barrier != nil:
|
if barrier != nil:
|
||||||
|
|
@ -239,7 +433,17 @@ proc wrapProcForSpawn*(owner: PSym; n: PNode; barrier: PNode = nil): PNode =
|
||||||
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
|
result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier)
|
||||||
barrierAsExpr = indirectAccess(castExpr, field, n.info)
|
barrierAsExpr = indirectAccess(castExpr, field, n.info)
|
||||||
|
|
||||||
|
var futField, futAsExpr: PNode = nil
|
||||||
|
if spawnKind == srFuture:
|
||||||
|
var field = newSym(skField, getIdent"fut", owner, n.info)
|
||||||
|
field.typ = retType
|
||||||
|
objType.addField(field)
|
||||||
|
futField = newDotExpr(scratchObj, field)
|
||||||
|
futAsExpr = indirectAccess(castExpr, field, n.info)
|
||||||
|
|
||||||
let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call,
|
let wrapper = createWrapperProc(fn, threadParam, argsParam, varSection, call,
|
||||||
barrierAsExpr)
|
barrierAsExpr, futAsExpr)
|
||||||
result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
|
result.add callCodeGenProc("nimSpawn", wrapper.newSymNode,
|
||||||
genAddrOf(scratchObj.newSymNode))
|
genAddrOf(scratchObj.newSymNode))
|
||||||
|
|
||||||
|
if spawnKind == srFuture: result.add futField
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,12 @@ proc semLocals(c: PContext, n: PNode): PNode =
|
||||||
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
if it.typ.skipTypes({tyGenericInst}).kind == tyVar: a = newDeref(a)
|
||||||
result.add(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 semShallowCopy(c: PContext, n: PNode, flags: TExprFlags): PNode
|
||||||
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
flags: TExprFlags): PNode =
|
flags: TExprFlags): PNode =
|
||||||
|
|
@ -130,5 +136,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
|
||||||
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
of mShallowCopy: result = semShallowCopy(c, n, flags)
|
||||||
of mNBindSym: result = semBindSym(c, n)
|
of mNBindSym: result = semBindSym(c, n)
|
||||||
of mLocals: result = semLocals(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
|
else: result = n
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,8 @@
|
||||||
|
|
||||||
## Semantic checking for 'parallel'.
|
## Semantic checking for 'parallel'.
|
||||||
|
|
||||||
# - codegen needs to support mSlice
|
# - codegen needs to support mSlice (+)
|
||||||
# - lowerings must not perform unnecessary copies
|
# - lowerings must not perform unnecessary copies (+)
|
||||||
# - slices should become "nocopy" to openArray (+)
|
# - slices should become "nocopy" to openArray (+)
|
||||||
# - need to perform bound checks (+)
|
# - need to perform bound checks (+)
|
||||||
#
|
#
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
# - what about 'f(a)'? --> f shouldn't have side effects anyway
|
# - what about 'f(a)'? --> f shouldn't have side effects anyway
|
||||||
# - passed arrays need to be ensured not to alias
|
# - passed arrays need to be ensured not to alias
|
||||||
# - passed slices need to be ensured to be disjoint (+)
|
# - passed slices need to be ensured to be disjoint (+)
|
||||||
# - output slices need special logic
|
# - output slices need special logic (+)
|
||||||
|
|
||||||
import
|
import
|
||||||
ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
|
ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
|
||||||
|
|
@ -94,23 +94,6 @@ proc getSlot(c: var AnalysisCtx; v: PSym): ptr MonotonicVar =
|
||||||
c.locals[L].v = v
|
c.locals[L].v = v
|
||||||
return addr(c.locals[L])
|
return addr(c.locals[L])
|
||||||
|
|
||||||
proc getRoot(n: PNode): PSym =
|
|
||||||
## ``getRoot`` takes a *path* ``n``. A path is an lvalue expression
|
|
||||||
## like ``obj.x[i].y``. The *root* of a path is the symbol that can be
|
|
||||||
## determined as the owner; ``obj`` in the example.
|
|
||||||
case n.kind
|
|
||||||
of nkSym:
|
|
||||||
if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}:
|
|
||||||
result = n.sym
|
|
||||||
of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr,
|
|
||||||
nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
|
|
||||||
result = getRoot(n.sons[0])
|
|
||||||
of nkHiddenStdConv, nkHiddenSubConv, nkConv:
|
|
||||||
result = getRoot(n.sons[1])
|
|
||||||
of nkCallKinds:
|
|
||||||
if getMagic(n) == mSlice: result = getRoot(n.sons[1])
|
|
||||||
else: discard
|
|
||||||
|
|
||||||
proc gatherArgs(c: var AnalysisCtx; n: PNode) =
|
proc gatherArgs(c: var AnalysisCtx; n: PNode) =
|
||||||
for i in 0.. <n.safeLen:
|
for i in 0.. <n.safeLen:
|
||||||
let root = getRoot n[i]
|
let root = getRoot n[i]
|
||||||
|
|
@ -184,8 +167,6 @@ proc overlap(m: TModel; x,y,c,d: PNode) =
|
||||||
of impNo: discard
|
of impNo: discard
|
||||||
|
|
||||||
proc stride(c: AnalysisCtx; n: PNode): BiggestInt =
|
proc stride(c: AnalysisCtx; n: PNode): BiggestInt =
|
||||||
# note: 0 if it cannot be determined is just right because then
|
|
||||||
# we analyse 'i..i' and 'i+0 .. i+0' and these are not disjoint!
|
|
||||||
if isLocal(n):
|
if isLocal(n):
|
||||||
let s = c.lookupSlot(n.sym)
|
let s = c.lookupSlot(n.sym)
|
||||||
if s >= 0 and c.locals[s].stride != nil:
|
if s >= 0 and c.locals[s].stride != nil:
|
||||||
|
|
@ -193,6 +174,20 @@ proc stride(c: AnalysisCtx; n: PNode): BiggestInt =
|
||||||
else:
|
else:
|
||||||
for i in 0 .. <n.safeLen: result += stride(c, n.sons[i])
|
for i in 0 .. <n.safeLen: result += stride(c, n.sons[i])
|
||||||
|
|
||||||
|
proc subStride(c: AnalysisCtx; n: PNode): PNode =
|
||||||
|
# substitute with stride:
|
||||||
|
if isLocal(n):
|
||||||
|
let s = c.lookupSlot(n.sym)
|
||||||
|
if s >= 0 and c.locals[s].stride != nil:
|
||||||
|
result = n +@ c.locals[s].stride.intVal
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
elif n.safeLen > 0:
|
||||||
|
result = shallowCopy(n)
|
||||||
|
for i in 0 .. <n.len: result.sons[i] = subStride(c, n.sons[i])
|
||||||
|
else:
|
||||||
|
result = n
|
||||||
|
|
||||||
proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
|
proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
|
||||||
# this is the only thing that we need to perform after we have traversed
|
# this is the only thing that we need to perform after we have traversed
|
||||||
# the whole tree so that the strides are available.
|
# the whole tree so that the strides are available.
|
||||||
|
|
@ -200,7 +195,7 @@ proc checkSlicesAreDisjoint(c: var AnalysisCtx) =
|
||||||
addLowerBoundAsFacts(c)
|
addLowerBoundAsFacts(c)
|
||||||
# Every slice used in a loop needs to be disjoint with itself:
|
# Every slice used in a loop needs to be disjoint with itself:
|
||||||
for x,a,b,id,inLoop in items(c.slices):
|
for x,a,b,id,inLoop in items(c.slices):
|
||||||
if inLoop: overlap(c.guards, a,b, a+@c.stride(a), b+@c.stride(b))
|
if inLoop: overlap(c.guards, a,b, c.subStride(a), c.subStride(b))
|
||||||
# Another tricky example is:
|
# Another tricky example is:
|
||||||
# while true:
|
# while true:
|
||||||
# spawn f(a[i])
|
# spawn f(a[i])
|
||||||
|
|
@ -283,23 +278,19 @@ proc analyseCall(c: var AnalysisCtx; n: PNode; op: PSym) =
|
||||||
|
|
||||||
proc analyseCase(c: var AnalysisCtx; n: PNode) =
|
proc analyseCase(c: var AnalysisCtx; n: PNode) =
|
||||||
analyse(c, n.sons[0])
|
analyse(c, n.sons[0])
|
||||||
#let oldState = c.locals.len
|
|
||||||
let oldFacts = c.guards.len
|
let oldFacts = c.guards.len
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
let branch = n.sons[i]
|
let branch = n.sons[i]
|
||||||
#setLen(c.locals, oldState)
|
|
||||||
setLen(c.guards, oldFacts)
|
setLen(c.guards, oldFacts)
|
||||||
addCaseBranchFacts(c.guards, n, i)
|
addCaseBranchFacts(c.guards, n, i)
|
||||||
for i in 0 .. <branch.len:
|
for i in 0 .. <branch.len:
|
||||||
analyse(c, branch.sons[i])
|
analyse(c, branch.sons[i])
|
||||||
#setLen(c.locals, oldState)
|
|
||||||
setLen(c.guards, oldFacts)
|
setLen(c.guards, oldFacts)
|
||||||
|
|
||||||
proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
||||||
analyse(c, n.sons[0].sons[0])
|
analyse(c, n.sons[0].sons[0])
|
||||||
let oldFacts = c.guards.len
|
let oldFacts = c.guards.len
|
||||||
addFact(c.guards, n.sons[0].sons[0])
|
addFact(c.guards, n.sons[0].sons[0])
|
||||||
#let oldState = c.locals.len
|
|
||||||
|
|
||||||
analyse(c, n.sons[0].sons[1])
|
analyse(c, n.sons[0].sons[1])
|
||||||
for i in 1.. <n.len:
|
for i in 1.. <n.len:
|
||||||
|
|
@ -309,10 +300,8 @@ proc analyseIf(c: var AnalysisCtx; n: PNode) =
|
||||||
addFactNeg(c.guards, n.sons[j].sons[0])
|
addFactNeg(c.guards, n.sons[j].sons[0])
|
||||||
if branch.len > 1:
|
if branch.len > 1:
|
||||||
addFact(c.guards, branch.sons[0])
|
addFact(c.guards, branch.sons[0])
|
||||||
#setLen(c.locals, oldState)
|
|
||||||
for i in 0 .. <branch.len:
|
for i in 0 .. <branch.len:
|
||||||
analyse(c, branch.sons[i])
|
analyse(c, branch.sons[i])
|
||||||
#setLen(c.locals, oldState)
|
|
||||||
setLen(c.guards, oldFacts)
|
setLen(c.guards, oldFacts)
|
||||||
|
|
||||||
proc analyse(c: var AnalysisCtx; n: PNode) =
|
proc analyse(c: var AnalysisCtx; n: PNode) =
|
||||||
|
|
@ -390,17 +379,40 @@ proc transformSlices(n: PNode): PNode =
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
proc transformSpawn(owner: PSym; n, barrier: PNode): PNode
|
||||||
|
proc transformSpawnSons(owner: PSym; n, barrier: PNode): PNode =
|
||||||
|
result = shallowCopy(n)
|
||||||
|
for i in 0 .. < n.len:
|
||||||
|
result.sons[i] = transformSpawn(owner, n.sons[i], barrier)
|
||||||
|
|
||||||
proc transformSpawn(owner: PSym; n, barrier: PNode): PNode =
|
proc transformSpawn(owner: PSym; n, barrier: PNode): PNode =
|
||||||
if n.kind in nkCallKinds:
|
case n.kind
|
||||||
if n[0].kind == nkSym:
|
of nkVarSection:
|
||||||
let op = n[0].sym
|
result = nil
|
||||||
if op.magic == mSpawn:
|
for it in n:
|
||||||
result = transformSlices(n)
|
let b = it.lastSon
|
||||||
return wrapProcForSpawn(owner, result[1], barrier)
|
if getMagic(b) == mSpawn:
|
||||||
|
if it.len != 3: localError(it.info, "invalid context for 'spawn'")
|
||||||
|
let m = transformSlices(b)
|
||||||
|
if result.isNil:
|
||||||
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
result.add n
|
||||||
|
result.add wrapProcForSpawn(owner, m[1], b.typ, barrier, it[0])
|
||||||
|
it.sons[it.len-1] = emptyNode
|
||||||
|
if result.isNil: result = n
|
||||||
|
of nkAsgn, nkFastAsgn:
|
||||||
|
let b = n[1]
|
||||||
|
if getMagic(b) == mSpawn:
|
||||||
|
let m = transformSlices(b)
|
||||||
|
return wrapProcForSpawn(owner, m[1], b.typ, barrier, n[0])
|
||||||
|
result = transformSpawnSons(owner, n, barrier)
|
||||||
|
of nkCallKinds:
|
||||||
|
if getMagic(n) == mSpawn:
|
||||||
|
result = transformSlices(n)
|
||||||
|
return wrapProcForSpawn(owner, result[1], n.typ, barrier, nil)
|
||||||
|
result = transformSpawnSons(owner, n, barrier)
|
||||||
elif n.safeLen > 0:
|
elif n.safeLen > 0:
|
||||||
result = shallowCopy(n)
|
result = transformSpawnSons(owner, n, barrier)
|
||||||
for i in 0 .. < n.len:
|
|
||||||
result.sons[i] = transformSpawn(owner, n.sons[i], barrier)
|
|
||||||
else:
|
else:
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
|
|
@ -440,3 +452,4 @@ proc liftParallel*(owner: PSym; n: PNode): PNode =
|
||||||
result.add callCodeGenProc("openBarrier", barrier)
|
result.add callCodeGenProc("openBarrier", barrier)
|
||||||
result.add transformSpawn(owner, body, barrier)
|
result.add transformSpawn(owner, body, barrier)
|
||||||
result.add callCodeGenProc("closeBarrier", barrier)
|
result.add callCodeGenProc("closeBarrier", barrier)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2748,7 +2748,7 @@ The following builtin procs cannot be overloaded for reasons of implementation
|
||||||
simplicity (they require specialized semantic checking)::
|
simplicity (they require specialized semantic checking)::
|
||||||
|
|
||||||
defined, definedInScope, compiles, low, high, sizeOf,
|
defined, definedInScope, compiles, low, high, sizeOf,
|
||||||
is, of, echo, shallowCopy, getAst
|
is, of, echo, shallowCopy, getAst, spawn
|
||||||
|
|
||||||
Thus they act more like keywords than like ordinary identifiers; unlike a
|
Thus they act more like keywords than like ordinary identifiers; unlike a
|
||||||
keyword however, a redefinition may `shadow`:idx: the definition in
|
keyword however, a redefinition may `shadow`:idx: the definition in
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,30 @@ proc closeBarrier*(b: ptr Barrier) {.compilerProc.} =
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type
|
type
|
||||||
|
AwaitInfo = object
|
||||||
|
cv: CondVar
|
||||||
|
idx: int
|
||||||
|
|
||||||
|
RawFuture* = ptr RawFutureObj ## untyped base class for 'Future[T]'
|
||||||
|
RawFutureObj {.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
|
||||||
|
ready, usesCondVar: bool
|
||||||
|
cv: CondVar #\
|
||||||
|
# for 'awaitAny' support
|
||||||
|
ai: ptr AwaitInfo
|
||||||
|
idx: int
|
||||||
|
data: PObject # we incRef and unref it to keep it alive
|
||||||
|
owner: ptr Worker
|
||||||
|
next: RawFuture
|
||||||
|
align: float64 # a float for proper alignment
|
||||||
|
|
||||||
|
Future* {.compilerProc.} [T] = ptr object of RawFutureObj
|
||||||
|
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 ``^``.
|
||||||
|
|
||||||
WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.}
|
WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.}
|
||||||
Worker = object
|
Worker = object
|
||||||
taskArrived: CondVar
|
taskArrived: CondVar
|
||||||
|
|
@ -75,6 +99,92 @@ type
|
||||||
ready: bool # put it here for correct alignment!
|
ready: bool # put it here for correct alignment!
|
||||||
initialized: bool # whether it has even been initialized
|
initialized: bool # whether it has even been initialized
|
||||||
shutdown: bool # the pool requests to shut down this worker thread
|
shutdown: bool # the pool requests to shut down this worker thread
|
||||||
|
futureLock: TLock
|
||||||
|
head: RawFuture
|
||||||
|
|
||||||
|
proc finished*(fut: RawFuture) =
|
||||||
|
## This MUST be called for every created future 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) =
|
||||||
|
var it = w.head
|
||||||
|
acquire(w.futureLock)
|
||||||
|
while it != nil:
|
||||||
|
let nxt = it.next
|
||||||
|
if it.usesCondVar: destroyCondVar(it.cv)
|
||||||
|
if it.data != nil: GC_unref(it.data)
|
||||||
|
dealloc(it)
|
||||||
|
it = nxt
|
||||||
|
w.head = nil
|
||||||
|
release(w.futureLock)
|
||||||
|
|
||||||
|
proc nimCreateFuture(owner: pointer; blobSize: int): RawFuture {.
|
||||||
|
compilerProc.} =
|
||||||
|
result = cast[RawFuture](alloc0(RawFutureObj.sizeof + blobSize))
|
||||||
|
result.owner = cast[ptr Worker](owner)
|
||||||
|
|
||||||
|
proc nimFutureCreateCondVar(fut: RawFuture) {.compilerProc.} =
|
||||||
|
fut.cv = createCondVar()
|
||||||
|
fut.usesCondVar = true
|
||||||
|
|
||||||
|
proc nimFutureSignal(fut: RawFuture) {.compilerProc.} =
|
||||||
|
assert fut.usesCondVar
|
||||||
|
signal(fut.cv)
|
||||||
|
|
||||||
|
proc await*[T](fut: Future[T]) =
|
||||||
|
## waits until the value for the future arrives.
|
||||||
|
if fut.usesCondVar: await(fut.cv)
|
||||||
|
|
||||||
|
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)
|
||||||
|
when T is string or T is seq or T is ref:
|
||||||
|
result = cast[T](fut.data)
|
||||||
|
else:
|
||||||
|
result = fut.payload
|
||||||
|
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.
|
||||||
|
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
|
||||||
|
else:
|
||||||
|
inc conflicts
|
||||||
|
if conflicts < futures.len:
|
||||||
|
await(ai.cv)
|
||||||
|
result = ai.idx
|
||||||
|
for i in 0 .. futures.high:
|
||||||
|
discard cas(addr futures[i].ai, addr ai, nil)
|
||||||
|
else:
|
||||||
|
result = -1
|
||||||
|
destroyCondVar(ai.cv)
|
||||||
|
|
||||||
proc nimArgsPassingDone(p: pointer) {.compilerProc.} =
|
proc nimArgsPassingDone(p: pointer) {.compilerProc.} =
|
||||||
let w = cast[ptr Worker](p)
|
let w = cast[ptr Worker](p)
|
||||||
|
|
@ -99,6 +209,7 @@ proc slave(w: ptr Worker) {.thread.} =
|
||||||
await(w.taskArrived)
|
await(w.taskArrived)
|
||||||
assert(not w.ready)
|
assert(not w.ready)
|
||||||
w.f(w, w.data)
|
w.f(w, w.data)
|
||||||
|
if w.head != nil: w.cleanFutures
|
||||||
if w.shutdown:
|
if w.shutdown:
|
||||||
w.shutdown = false
|
w.shutdown = false
|
||||||
atomicDec currentPoolSize
|
atomicDec currentPoolSize
|
||||||
|
|
@ -119,6 +230,7 @@ var
|
||||||
proc activateThread(i: int) {.noinline.} =
|
proc activateThread(i: int) {.noinline.} =
|
||||||
workersData[i].taskArrived = createCondVar()
|
workersData[i].taskArrived = createCondVar()
|
||||||
workersData[i].taskStarted = createCondVar()
|
workersData[i].taskStarted = createCondVar()
|
||||||
|
initLock workersData[i].futureLock
|
||||||
workersData[i].initialized = true
|
workersData[i].initialized = true
|
||||||
createThread(workers[i], slave, addr(workersData[i]))
|
createThread(workers[i], slave, addr(workersData[i]))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -209,12 +209,12 @@ when defined(windows) and not defined(gcc):
|
||||||
proc interlockedCompareExchange(p: pointer; exchange, comparand: int32): int32
|
proc interlockedCompareExchange(p: pointer; exchange, comparand: int32): int32
|
||||||
{.importc: "InterlockedCompareExchange", header: "<windows.h>", cdecl.}
|
{.importc: "InterlockedCompareExchange", header: "<windows.h>", cdecl.}
|
||||||
|
|
||||||
proc cas*[T: bool|int](p: ptr T; oldValue, newValue: T): bool =
|
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool =
|
||||||
interlockedCompareExchange(p, newValue.int32, oldValue.int32) != 0
|
interlockedCompareExchange(p, newValue.int32, oldValue.int32) != 0
|
||||||
|
# XXX fix for 64 bit build
|
||||||
else:
|
else:
|
||||||
# this is valid for GCC and Intel C++
|
# this is valid for GCC and Intel C++
|
||||||
proc cas*[T: bool|int](p: ptr T; oldValue, newValue: T): bool
|
proc cas*[T: bool|int|ptr](p: ptr T; oldValue, newValue: T): bool
|
||||||
{.importc: "__sync_bool_compare_and_swap", nodecl.}
|
{.importc: "__sync_bool_compare_and_swap", nodecl.}
|
||||||
# XXX is this valid for 'int'?
|
# XXX is this valid for 'int'?
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
|
discard """
|
||||||
|
outputsub: "EVEN 28"
|
||||||
|
"""
|
||||||
|
|
||||||
import threadpool
|
import threadpool
|
||||||
|
|
||||||
proc f(a: openArray[int]) =
|
proc odd(a: int) = echo "ODD ", a
|
||||||
for x in a: echo x
|
proc even(a: int) = echo "EVEN ", a
|
||||||
|
|
||||||
proc f(a: int) = echo a
|
|
||||||
|
|
||||||
proc main() =
|
proc main() =
|
||||||
var a: array[0..30, int]
|
var a: array[0..30, int]
|
||||||
|
for i in low(a)..high(a): a[i] = i
|
||||||
parallel:
|
parallel:
|
||||||
#spawn f(a[0..15])
|
|
||||||
#spawn f(a[16..30])
|
|
||||||
var i = 0
|
var i = 0
|
||||||
while i <= 29:
|
while i <= 29:
|
||||||
spawn f(a[i])
|
spawn even(a[i])
|
||||||
spawn f(a[i+1])
|
spawn odd(a[i+1])
|
||||||
inc i, 2
|
inc i, 2
|
||||||
# is correct here
|
# is correct here
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
discard """
|
discard """
|
||||||
errormsg: "cannot prove: i + 1 <= 30"
|
errormsg: "can prove: i + 1 > 30"
|
||||||
line: 21
|
line: 21
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue