The problem is that threads _go to sleep while still holding a lock_.
Well, the real problem is that afaik there is no guarantee that this code will
ever finish, because the scheduler is not required to ever wake the right
thread when a bunch of other threads are still waiting. But anyway, Moving the
`sleep` call behind the lock seems to work fine in practice:
import unicode, threadpool, locks, os
proc lenU*(s: string): int =
result = s.runeLen
proc charAtPosU*(s: string, pos: int): string =
assert(pos >= 0 and pos < s.runeLen)
result = s.runeAtPos(pos).toUTF8()
proc multithreadedPrint(sMsg: string, nCount: int) =
var
nLen = sMsg.lenU
nCallsTotal = 0
nCallsCur = 0
lk: Lock
res = 0
proc worker(c: string, value: int) {.gcsafe.}=
while true:
var found = true
acquire(lk)
try:
if nCallsCur == nCallsTotal:
return
if res == value:
inc res
res = res mod nLen
inc nCallsCur
stdout.write c
else:
found = false
finally:
release(lk)
if not found: sleep(1)
if nLen > 0:
if nLen > MaxDistinguishedThread:
echo "Your string is too long. Maximum allowed is ",
MaxDistinguishedThread, "!"
return
setMinPoolSize(nLen)
setMaxPoolSize(nLen)
initLock(lk)
try:
nCallsTotal = nLen * nCount
echo("Total threads: ", nLen)
echo("Total calls: ", nCallsTotal)
for i in 0..<nLen:
spawn worker(sMsg.charAtPosU(i), i)
sync()
finally:
deinitLock(lk)
multithreadedPrint("0123456789", 2)