diff options
Diffstat (limited to 'lib/core/locks.nim')
-rw-r--r-- | lib/core/locks.nim | 158 |
1 files changed, 158 insertions, 0 deletions
diff --git a/lib/core/locks.nim b/lib/core/locks.nim new file mode 100644 index 000000000..1fffb8e0a --- /dev/null +++ b/lib/core/locks.nim @@ -0,0 +1,158 @@ +# +# +# Nimrod's Runtime Library +# (c) Copyright 2011 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains Nimrod's support for locks and condition vars. +## If the symbol ``preventDeadlocks`` is defined +## (compiled with ``-d:preventDeadlocks``) special logic is added to +## every ``acquire``, ``tryAcquire`` and ``release`` action that ensures at +## runtime that no deadlock can occur. This is achieved by forcing a thread +## to release its locks should it be part of a deadlock. This thread then +## re-acquires its locks and proceeds. + +include "lib/system/syslocks" + +type + TLock* = TSysLock ## Nimrod lock; whether this is re-entrant + ## or not is unspecified! + TCond* = TSysCond ## Nimrod condition variable + +const + noDeadlocks = defined(preventDeadlocks) + maxLocksPerThread* = 10 ## max number of locks a thread can hold + ## at the same time; this limit is only relevant + ## when compiled with ``-d:preventDeadlocks``. + +var + deadlocksPrevented*: int ## counts the number of times a + ## deadlock has been prevented + +when noDeadlocks: + var + locksLen {.threadvar.}: int + locks {.threadvar.}: array [0..MaxLocksPerThread-1, pointer] + + proc OrderedLocks(): bool = + for i in 0 .. locksLen-2: + if locks[i] >= locks[i+1]: return false + result = true + +proc InitLock*(lock: var TLock) {.inline.} = + ## Initializes the given lock. + InitSysLock(lock) + +proc DeinitLock*(lock: var TLock) {.inline.} = + ## Frees the resources associated with the lock. + DeinitSys(lock) + +proc TryAcquire*(lock: var TLock): bool {.inline.} = + ## Tries to acquire the given lock. Returns `true` on success. + result = TryAcquireSys(lock) + when noDeadlocks: + if not result: return + # we have to add it to the ordered list. Oh, and we might fail if + # there is no space in the array left ... + if locksLen >= len(locks): + ReleaseSys(lock) + raise newException(EResourceExhausted, "cannot acquire additional lock") + # find the position to add: + var p = addr(lock) + var L = locksLen-1 + var i = 0 + while i <= L: + assert locks[i] != nil + if locks[i] < p: inc(i) # in correct order + elif locks[i] == p: return # thread already holds lock + else: + # do the crazy stuff here: + while L >= i: + locks[L+1] = locks[L] + dec L + locks[i] = p + inc(locksLen) + assert OrderedLocks() + return + # simply add to the end: + locks[locksLen] = p + inc(locksLen) + assert OrderedLocks() + +proc Acquire*(lock: var TLock) = + ## Acquires the given lock. + when nodeadlocks: + var p = addr(lock) + var L = locksLen-1 + var i = 0 + while i <= L: + assert locks[i] != nil + if locks[i] < p: inc(i) # in correct order + elif locks[i] == p: return # thread already holds lock + else: + # do the crazy stuff here: + if locksLen >= len(locks): + raise newException(EResourceExhausted, + "cannot acquire additional lock") + while L >= i: + ReleaseSys(cast[ptr TSysLock](locks[L])[]) + locks[L+1] = locks[L] + dec L + # acquire the current lock: + AcquireSys(lock) + locks[i] = p + inc(locksLen) + # acquire old locks in proper order again: + L = locksLen-1 + inc i + while i <= L: + AcquireSys(cast[ptr TSysLock](locks[i])[]) + inc(i) + # DANGER: We can only modify this global var if we gained every lock! + # NO! We need an atomic increment. Crap. + discard system.atomicInc(deadlocksPrevented, 1) + assert OrderedLocks() + return + + # simply add to the end: + if locksLen >= len(locks): + raise newException(EResourceExhausted, "cannot acquire additional lock") + AcquireSys(lock) + locks[locksLen] = p + inc(locksLen) + assert OrderedLocks() + else: + AcquireSys(lock) + +proc Release*(lock: var TLock) = + ## Releases the given lock. + when nodeadlocks: + var p = addr(lock) + var L = locksLen + for i in countdown(L-1, 0): + if locks[i] == p: + for j in i..L-2: locks[j] = locks[j+1] + dec locksLen + break + ReleaseSys(lock) + + +proc InitCond*(cond: var TCond) {.inline.} = + ## Initializes the given condition variable. + InitSysCond(cond) + +proc DeinitCond*(cond: var TCond) {.inline.} = + ## Frees the resources associated with the lock. + DeinitSysCond(cond) + +proc wait*(cond: var TCond, lock: var TLock) {.inline.} = + ## waits on the condition variable `cond`. + WaitSysCond(cond, lock) + +proc signal*(cond: var TCond) {.inline.} = + ## sends a signal to the condition variable `cond`. + signalSysCond(cond) + |