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)
    

Reply via email to