diff options
Diffstat (limited to 'lib/core/locks.nim')
-rw-r--r-- | lib/core/locks.nim | 214 |
1 files changed, 69 insertions, 145 deletions
diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 071bde93a..523727479 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -1,166 +1,90 @@ # # -# Nimrod's Runtime Library -# (c) Copyright 2012 Andreas Rumpf +# Nim's Runtime Library +# (c) Copyright 2015 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. +## This module contains Nim's support for locks and condition vars. -include "system/syslocks" +#[ +for js, for now we treat locks as noop's to avoid pushing `when defined(js)` +in client code that uses locks. +]# + +when not compileOption("threads") and not defined(nimdoc): + when false: # fix #12330 + {.error: "Locks requires --threads:on option.".} + +import std/private/syslocks type - TLock* = TSysLock ## Nimrod lock; whether this is re-entrant - ## or not is unspecified! However, compilation - ## in preventDeadlocks-mode guarantees re-entrancy. - TCond* = TSysCond ## Nimrod condition variable - - FLock* = object of TEffect ## effect that denotes that some lock operation - ## is performed - FAquireLock* = object of FLock ## effect that denotes that some lock is - ## aquired - FReleaseLock* = object of FLock ## effect that denotes that some lock is - ## released - -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.} = + Lock* = SysLock ## Nim lock; whether this is re-entrant + ## or not is unspecified! + Cond* = SysCond ## Nim condition variable + +{.push stackTrace: off.} + + +proc `$`*(lock: Lock): string = + # workaround bug #14873 + result = "()" + +proc initLock*(lock: var Lock) {.inline.} = ## Initializes the given lock. - InitSysLock(lock) + when not defined(js): + initSysLock(lock) -proc DeinitLock*(lock: var TLock) {.inline.} = +proc deinitLock*(lock: Lock) {.inline.} = ## Frees the resources associated with the lock. - DeinitSys(lock) + deinitSys(lock) -proc TryAcquire*(lock: var TLock): bool {.tags: [FAquireLock].} = +proc tryAcquire*(lock: var Lock): 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) {.tags: [FAquireLock].} = + result = tryAcquireSys(lock) + +proc acquire*(lock: var Lock) {.inline.} = ## 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) {.tags: [FReleaseLock].} = + when not defined(js): + acquireSys(lock) + +proc release*(lock: var Lock) {.inline.} = ## 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.} = + when not defined(js): + releaseSys(lock) + + +proc initCond*(cond: var Cond) {.inline.} = ## Initializes the given condition variable. - InitSysCond(cond) + 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`. +proc deinitCond*(cond: Cond) {.inline.} = + ## Frees the resources associated with the condition variable. + deinitSysCond(cond) + +proc wait*(cond: var Cond, lock: var Lock) {.inline.} = + ## Waits on the condition variable `cond`. + waitSysCond(cond, lock) + +proc signal*(cond: var Cond) {.inline.} = + ## Sends a signal to the condition variable `cond`. signalSysCond(cond) +proc broadcast*(cond: var Cond) {.inline.} = + ## Unblocks all threads currently blocked on the + ## specified condition variable `cond`. + broadcastSysCond(cond) + +template withLock*(a: Lock, body: untyped) = + ## Acquires the given lock, executes the statements in body and + ## releases the lock after the statements finish executing. + acquire(a) + {.locks: [a].}: + try: + body + finally: + release(a) + +{.pop.} |