renames threadpool.await to blockUntil; refs #7853
This commit is contained in:
parent
741f95e2d6
commit
83c89197f3
5 changed files with 36 additions and 32 deletions
|
|
@ -63,6 +63,10 @@
|
||||||
- ``lineInfoObj`` now returns absolute path instead of project path.
|
- ``lineInfoObj`` now returns absolute path instead of project path.
|
||||||
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
|
It's used by ``lineInfo``, ``check``, ``expect``, ``require``, etc.
|
||||||
|
|
||||||
|
- `threadpool`'s `await` and derivatives have been renamed to `blockUntil`
|
||||||
|
to avoid confusions with `await` from the `async` macro.
|
||||||
|
|
||||||
|
|
||||||
#### Breaking changes in the compiler
|
#### Breaking changes in the compiler
|
||||||
|
|
||||||
- The undocumented ``#? braces`` parsing mode was removed.
|
- The undocumented ``#? braces`` parsing mode was removed.
|
||||||
|
|
|
||||||
|
|
@ -7877,7 +7877,7 @@ that ``spawn`` takes is restricted:
|
||||||
|
|
||||||
``spawn`` executes the passed expression on the thread pool and returns
|
``spawn`` executes the passed expression on the thread pool and returns
|
||||||
a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading
|
a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading
|
||||||
with the ``^`` operator is **blocking**. However, one can use ``awaitAny`` to
|
with the ``^`` operator is **blocking**. However, one can use ``blockUntilAny`` to
|
||||||
wait on multiple flow variables at the same time:
|
wait on multiple flow variables at the same time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -7888,10 +7888,10 @@ wait on multiple flow variables at the same time:
|
||||||
var responses = newSeq[FlowVarBase](3)
|
var responses = newSeq[FlowVarBase](3)
|
||||||
for i in 0..2:
|
for i in 0..2:
|
||||||
responses[i] = spawn tellServer(Update, "key", "value")
|
responses[i] = spawn tellServer(Update, "key", "value")
|
||||||
var index = awaitAny(responses)
|
var index = blockUntilAny(responses)
|
||||||
assert index >= 0
|
assert index >= 0
|
||||||
responses.del(index)
|
responses.del(index)
|
||||||
discard awaitAny(responses)
|
discard blockUntilAny(responses)
|
||||||
|
|
||||||
Data flow variables ensure that no data races
|
Data flow variables ensure that no data races
|
||||||
are possible. Due to technical limitations not every type ``T`` is possible in
|
are possible. Due to technical limitations not every type ``T`` is possible in
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ Spawn statement
|
||||||
A standalone ``spawn`` statement is a simple construct. It executes
|
A standalone ``spawn`` statement is a simple construct. It executes
|
||||||
the passed expression on the thread pool and returns a `data flow variable`:idx:
|
the passed expression on the thread pool and returns a `data flow variable`:idx:
|
||||||
``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is
|
``FlowVar[T]`` that can be read from. The reading with the ``^`` operator is
|
||||||
**blocking**. However, one can use ``awaitAny`` to wait on multiple flow
|
**blocking**. However, one can use ``blockUntilAny`` to wait on multiple flow
|
||||||
variables at the same time:
|
variables at the same time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
@ -36,10 +36,10 @@ variables at the same time:
|
||||||
var responses = newSeq[FlowVarBase](3)
|
var responses = newSeq[FlowVarBase](3)
|
||||||
for i in 0..2:
|
for i in 0..2:
|
||||||
responses[i] = spawn tellServer(Update, "key", "value")
|
responses[i] = spawn tellServer(Update, "key", "value")
|
||||||
var index = awaitAny(responses)
|
var index = blockUntilAny(responses)
|
||||||
assert index >= 0
|
assert index >= 0
|
||||||
responses.del(index)
|
responses.del(index)
|
||||||
discard awaitAny(responses)
|
discard blockUntilAny(responses)
|
||||||
|
|
||||||
Data flow variables ensure that no data races
|
Data flow variables ensure that no data races
|
||||||
are possible. Due to technical limitations not every type ``T`` is possible in
|
are possible. Due to technical limitations not every type ``T`` is possible in
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ proc destroySemaphore(cv: var Semaphore) {.inline.} =
|
||||||
deinitCond(cv.c)
|
deinitCond(cv.c)
|
||||||
deinitLock(cv.L)
|
deinitLock(cv.L)
|
||||||
|
|
||||||
proc await(cv: var Semaphore) =
|
proc blockUntil(cv: var Semaphore) =
|
||||||
acquire(cv.L)
|
acquire(cv.L)
|
||||||
while cv.counter <= 0:
|
while cv.counter <= 0:
|
||||||
wait(cv.c, cv.L)
|
wait(cv.c, cv.L)
|
||||||
|
|
@ -81,7 +81,7 @@ proc closeBarrier(b: ptr Barrier) {.compilerProc.} =
|
||||||
fence()
|
fence()
|
||||||
b.interest = true
|
b.interest = true
|
||||||
fence()
|
fence()
|
||||||
while b.left != b.entered: await(b.cv)
|
while b.left != b.entered: blockUntil(b.cv)
|
||||||
destroySemaphore(b.cv)
|
destroySemaphore(b.cv)
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
@ -97,7 +97,7 @@ type
|
||||||
FlowVarBaseObj = object of RootObj
|
FlowVarBaseObj = object of RootObj
|
||||||
ready, usesSemaphore, awaited: bool
|
ready, usesSemaphore, awaited: bool
|
||||||
cv: Semaphore #\
|
cv: Semaphore #\
|
||||||
# for 'awaitAny' support
|
# for 'blockUntilAny' support
|
||||||
ai: ptr AwaitInfo
|
ai: ptr AwaitInfo
|
||||||
idx: int
|
idx: int
|
||||||
data: pointer # we incRef and unref it to keep it alive; note this MUST NOT
|
data: pointer # we incRef and unref it to keep it alive; note this MUST NOT
|
||||||
|
|
@ -128,12 +128,12 @@ type
|
||||||
q: ToFreeQueue
|
q: ToFreeQueue
|
||||||
readyForTask: Semaphore
|
readyForTask: Semaphore
|
||||||
|
|
||||||
proc await*(fv: FlowVarBase) =
|
proc blockUntil*(fv: FlowVarBase) =
|
||||||
## waits until the value for the flowVar arrives. Usually it is not necessary
|
## waits until the value for the flowVar arrives. Usually it is not necessary
|
||||||
## to call this explicitly.
|
## to call this explicitly.
|
||||||
if fv.usesSemaphore and not fv.awaited:
|
if fv.usesSemaphore and not fv.awaited:
|
||||||
fv.awaited = true
|
fv.awaited = true
|
||||||
await(fv.cv)
|
blockUntil(fv.cv)
|
||||||
destroySemaphore(fv.cv)
|
destroySemaphore(fv.cv)
|
||||||
|
|
||||||
proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
|
proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
|
||||||
|
|
@ -141,7 +141,7 @@ proc selectWorker(w: ptr Worker; fn: WorkerProc; data: pointer): bool =
|
||||||
w.data = data
|
w.data = data
|
||||||
w.f = fn
|
w.f = fn
|
||||||
signal(w.taskArrived)
|
signal(w.taskArrived)
|
||||||
await(w.taskStarted)
|
blockUntil(w.taskStarted)
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc cleanFlowVars(w: ptr Worker) =
|
proc cleanFlowVars(w: ptr Worker) =
|
||||||
|
|
@ -176,11 +176,11 @@ proc attach(fv: FlowVarBase; i: int): bool =
|
||||||
release(fv.cv.L)
|
release(fv.cv.L)
|
||||||
|
|
||||||
proc finished(fv: FlowVarBase) =
|
proc finished(fv: FlowVarBase) =
|
||||||
doAssert fv.ai.isNil, "flowVar is still attached to an 'awaitAny'"
|
doAssert fv.ai.isNil, "flowVar is still attached to an 'blockUntilAny'"
|
||||||
# we have to protect against the rare cases where the owner of the flowVar
|
# we have to protect against the rare cases where the owner of the flowVar
|
||||||
# simply disregards the flowVar and yet the "flowVar" has not yet written
|
# simply disregards the flowVar and yet the "flowVar" has not yet written
|
||||||
# anything to it:
|
# anything to it:
|
||||||
await(fv)
|
blockUntil(fv)
|
||||||
if fv.data.isNil: return
|
if fv.data.isNil: return
|
||||||
let owner = cast[ptr Worker](fv.owner)
|
let owner = cast[ptr Worker](fv.owner)
|
||||||
let q = addr(owner.q)
|
let q = addr(owner.q)
|
||||||
|
|
@ -189,7 +189,7 @@ proc finished(fv: FlowVarBase) =
|
||||||
#echo "EXHAUSTED!"
|
#echo "EXHAUSTED!"
|
||||||
release(q.lock)
|
release(q.lock)
|
||||||
wakeupWorkerToProcessQueue(owner)
|
wakeupWorkerToProcessQueue(owner)
|
||||||
await(q.empty)
|
blockUntil(q.empty)
|
||||||
acquire(q.lock)
|
acquire(q.lock)
|
||||||
q.data[q.len] = cast[pointer](fv.data)
|
q.data[q.len] = cast[pointer](fv.data)
|
||||||
inc q.len
|
inc q.len
|
||||||
|
|
@ -220,7 +220,7 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
|
||||||
## to ``action``. Note that due to Nim's parameter passing semantics this
|
## to ``action``. Note that due to Nim's parameter passing semantics this
|
||||||
## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
|
## means that ``T`` doesn't need to be copied and so ``awaitAndThen`` can
|
||||||
## sometimes be more efficient than ``^``.
|
## sometimes be more efficient than ``^``.
|
||||||
await(fv)
|
blockUntil(fv)
|
||||||
when T is string or T is seq:
|
when T is string or T is seq:
|
||||||
action(cast[T](fv.data))
|
action(cast[T](fv.data))
|
||||||
elif T is ref:
|
elif T is ref:
|
||||||
|
|
@ -231,29 +231,29 @@ proc awaitAndThen*[T](fv: FlowVar[T]; action: proc (x: T) {.closure.}) =
|
||||||
|
|
||||||
proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
|
proc unsafeRead*[T](fv: FlowVar[ref T]): ptr T =
|
||||||
## blocks until the value is available and then returns this value.
|
## blocks until the value is available and then returns this value.
|
||||||
await(fv)
|
blockUntil(fv)
|
||||||
result = cast[ptr T](fv.data)
|
result = cast[ptr T](fv.data)
|
||||||
|
|
||||||
proc `^`*[T](fv: FlowVar[ref T]): ref T =
|
proc `^`*[T](fv: FlowVar[ref T]): ref T =
|
||||||
## blocks until the value is available and then returns this value.
|
## blocks until the value is available and then returns this value.
|
||||||
await(fv)
|
blockUntil(fv)
|
||||||
let src = cast[ref T](fv.data)
|
let src = cast[ref T](fv.data)
|
||||||
deepCopy result, src
|
deepCopy result, src
|
||||||
|
|
||||||
proc `^`*[T](fv: FlowVar[T]): T =
|
proc `^`*[T](fv: FlowVar[T]): T =
|
||||||
## blocks until the value is available and then returns this value.
|
## blocks until the value is available and then returns this value.
|
||||||
await(fv)
|
blockUntil(fv)
|
||||||
when T is string or T is seq:
|
when T is string or T is seq:
|
||||||
# XXX closures? deepCopy?
|
# XXX closures? deepCopy?
|
||||||
result = cast[T](fv.data)
|
result = cast[T](fv.data)
|
||||||
else:
|
else:
|
||||||
result = fv.blob
|
result = fv.blob
|
||||||
|
|
||||||
proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
|
proc blockUntilAny*(flowVars: openArray[FlowVarBase]): int =
|
||||||
## awaits any of the given flowVars. Returns the index of one flowVar for
|
## awaits any of the given flowVars. Returns the index of one flowVar for
|
||||||
## which a value arrived. A flowVar only supports one call to 'awaitAny' at
|
## which a value arrived. A flowVar only supports one call to 'blockUntilAny' at
|
||||||
## the same time. That means if you awaitAny([a,b]) and awaitAny([b,c]) the second
|
## the same time. That means if you blockUntilAny([a,b]) and blockUntilAny([b,c]) the second
|
||||||
## call will only await 'c'. If there is no flowVar left to be able to wait
|
## call will only blockUntil 'c'. If there is no flowVar left to be able to wait
|
||||||
## on, -1 is returned.
|
## on, -1 is returned.
|
||||||
## **Note**: This results in non-deterministic behaviour and should be avoided.
|
## **Note**: This results in non-deterministic behaviour and should be avoided.
|
||||||
var ai: AwaitInfo
|
var ai: AwaitInfo
|
||||||
|
|
@ -269,7 +269,7 @@ proc awaitAny*(flowVars: openArray[FlowVarBase]): int =
|
||||||
inc conflicts
|
inc conflicts
|
||||||
if conflicts < flowVars.len:
|
if conflicts < flowVars.len:
|
||||||
if result < 0:
|
if result < 0:
|
||||||
await(ai.cv)
|
blockUntil(ai.cv)
|
||||||
result = ai.idx
|
result = ai.idx
|
||||||
for i in 0 .. flowVars.high:
|
for i in 0 .. flowVars.high:
|
||||||
discard cas(addr flowVars[i].ai, addr ai, nil)
|
discard cas(addr flowVars[i].ai, addr ai, nil)
|
||||||
|
|
@ -326,7 +326,7 @@ proc slave(w: ptr Worker) {.thread.} =
|
||||||
w.ready = true
|
w.ready = true
|
||||||
readyWorker = w
|
readyWorker = w
|
||||||
signal(gSomeReady)
|
signal(gSomeReady)
|
||||||
await(w.taskArrived)
|
blockUntil(w.taskArrived)
|
||||||
# XXX Somebody needs to look into this (why does this assertion fail
|
# XXX Somebody needs to look into this (why does this assertion fail
|
||||||
# in Visual Studio?)
|
# in Visual Studio?)
|
||||||
when not defined(vcc) and not defined(tcc): assert(not w.ready)
|
when not defined(vcc) and not defined(tcc): assert(not w.ready)
|
||||||
|
|
@ -351,7 +351,7 @@ proc distinguishedSlave(w: ptr Worker) {.thread.} =
|
||||||
else:
|
else:
|
||||||
w.ready = true
|
w.ready = true
|
||||||
signal(w.readyForTask)
|
signal(w.readyForTask)
|
||||||
await(w.taskArrived)
|
blockUntil(w.taskArrived)
|
||||||
assert(not w.ready)
|
assert(not w.ready)
|
||||||
w.f(w, w.data)
|
w.f(w, w.data)
|
||||||
if w.q.len != 0: w.cleanFlowVars
|
if w.q.len != 0: w.cleanFlowVars
|
||||||
|
|
@ -499,7 +499,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
|
||||||
# on the current thread instead.
|
# on the current thread instead.
|
||||||
var self = addr(workersData[localThreadId-1])
|
var self = addr(workersData[localThreadId-1])
|
||||||
fn(self, data)
|
fn(self, data)
|
||||||
await(self.taskStarted)
|
blockUntil(self.taskStarted)
|
||||||
return
|
return
|
||||||
|
|
||||||
if isSlave:
|
if isSlave:
|
||||||
|
|
@ -524,7 +524,7 @@ proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} =
|
||||||
|
|
||||||
inc numSlavesWaiting
|
inc numSlavesWaiting
|
||||||
|
|
||||||
await(gSomeReady)
|
blockUntil(gSomeReady)
|
||||||
|
|
||||||
if isSlave:
|
if isSlave:
|
||||||
withLock numSlavesLock:
|
withLock numSlavesLock:
|
||||||
|
|
@ -542,7 +542,7 @@ proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} =
|
||||||
release(distinguishedLock)
|
release(distinguishedLock)
|
||||||
while true:
|
while true:
|
||||||
if selectWorker(addr(distinguishedData[id]), fn, data): break
|
if selectWorker(addr(distinguishedData[id]), fn, data): break
|
||||||
await(distinguishedData[id].readyForTask)
|
blockUntil(distinguishedData[id].readyForTask)
|
||||||
|
|
||||||
|
|
||||||
proc sync*() =
|
proc sync*() =
|
||||||
|
|
@ -555,7 +555,7 @@ proc sync*() =
|
||||||
if not allReady: break
|
if not allReady: break
|
||||||
allReady = allReady and workersData[i].ready
|
allReady = allReady and workersData[i].ready
|
||||||
if allReady: break
|
if allReady: break
|
||||||
await(gSomeReady)
|
blockUntil(gSomeReady)
|
||||||
inc toRelease
|
inc toRelease
|
||||||
|
|
||||||
for i in 0 ..< toRelease:
|
for i in 0 ..< toRelease:
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,12 @@ var results: seq[int] = @[]
|
||||||
for i in 0 .. durations.high:
|
for i in 0 .. durations.high:
|
||||||
tasks.add spawn timer(durations[i])
|
tasks.add spawn timer(durations[i])
|
||||||
|
|
||||||
var index = awaitAny(tasks)
|
var index = blockUntilAny(tasks)
|
||||||
while index != -1:
|
while index != -1:
|
||||||
results.add ^cast[FlowVar[int]](tasks[index])
|
results.add ^cast[FlowVar[int]](tasks[index])
|
||||||
tasks.del(index)
|
tasks.del(index)
|
||||||
#echo repr results
|
#echo repr results
|
||||||
index = awaitAny(tasks)
|
index = blockUntilAny(tasks)
|
||||||
|
|
||||||
doAssert results.len == 5
|
doAssert results.len == 5
|
||||||
doAssert 1000 in results
|
doAssert 1000 in results
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue