summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rwxr-xr-xcompiler/ccgstmts.nim12
-rwxr-xr-xdoc/lib.txt3
-rw-r--r--lib/core/locks.nim158
-rwxr-xr-xlib/pure/os.nim4
-rwxr-xr-xlib/system/threads.nim120
-rwxr-xr-xtests/accept/run/tnodeadlocks.nim4
-rwxr-xr-xtests/threads/tthreadanalysis.nim3
-rwxr-xr-xtests/threads/tthreadanalysis2.nim3
-rwxr-xr-xtodo.txt6
-rwxr-xr-xweb/news.txt2
10 files changed, 177 insertions, 138 deletions
diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim
index df843bc38..c12b93b4d 100755
--- a/compiler/ccgstmts.nim
+++ b/compiler/ccgstmts.nim
@@ -30,18 +30,14 @@ proc genLineDir(p: BProc, t: PNode) =
         [toRope(line), makeCString(toFilename(t.info).extractFilename)])
 
 proc genVarTuple(p: BProc, n: PNode) = 
-  var 
-    L: int
-    v: PSym
-    tup, field: TLoc
-    t: PType
+  var tup, field: TLoc
   if n.kind != nkVarTuple: InternalError(n.info, "genVarTuple")
-  L = sonsLen(n)
+  var L = sonsLen(n)
   genLineDir(p, n)
   initLocExpr(p, n.sons[L - 1], tup)
-  t = tup.t
+  var t = tup.t
   for i in countup(0, L - 3): 
-    v = n.sons[i].sym
+    var v = n.sons[i].sym
     if sfGlobal in v.flags: 
       assignGlobalVar(p, v)
       genObjectInit(p, cpsInit, v.typ, v.loc, true)
diff --git a/doc/lib.txt b/doc/lib.txt
index 942e2de26..7595028db 100755
--- a/doc/lib.txt
+++ b/doc/lib.txt
@@ -39,6 +39,9 @@ Core
   Nimrod message passing support for threads. **Note**: This is part of the 
   system module. Do not import it explicitely.
 
+* `locks <locks.html>`_
+  Locks and condition variables for Nimrod.
+
 * `macros <macros.html>`_
   Contains the AST API and documentation of Nimrod for writing macros.
 
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)
+
diff --git a/lib/pure/os.nim b/lib/pure/os.nim
index feb2de977..ccbf98828 100755
--- a/lib/pure/os.nim
+++ b/lib/pure/os.nim
@@ -516,12 +516,12 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1".} =
   ## Checks whether a given path is absolute.
   ##
   ## on Windows, network paths are considered absolute too.
-  var len = len(path)
   when doslike:
+    var len = len(path)
     result = (len > 1 and path[0] in {'/', '\\'}) or
              (len > 2 and path[0] in Letters and path[1] == ':')
   elif defined(macos):
-    result = len > 0 and path[0] != ':'
+    result = path.len > 0 and path[0] != ':'
   elif defined(RISCOS):
     result = path[0] == '$'
   elif defined(posix):
diff --git a/lib/system/threads.nim b/lib/system/threads.nim
index 39e5d3908..4ec1239a9 100755
--- a/lib/system/threads.nim
+++ b/lib/system/threads.nim
@@ -21,6 +21,8 @@
 ##
 ## .. code-block:: nimrod
 ##
+##  import locks
+##
 ##  var
 ##    thr: array [0..4, TThread[tuple[a,b: int]]]
 ##    L: TLock
@@ -39,8 +41,6 @@
   
 const
   maxRegisters = 256 # don't think there is an arch with more registers
-  maxLocksPerThread = 10 ## max number of locks a thread can hold
-                         ## at the same time
   useStackMaskHack = false ## use the stack mask hack for better performance
   StackGuardSize = 4096
   ThreadStackMask = 1024*256*sizeof(int)-1
@@ -251,7 +251,7 @@ when not defined(useNimRtl):
 type
   TThread* {.pure, final.}[TMsg] =
       object of TGcThread ## Nimrod thread. A thread is a heavy object (~14K)
-                          ## that should not be part of a message! Use
+                          ## that **must not** be part of a message! Use
                           ## a ``TThreadId`` for that.
     emptyFn: proc ()
     dataFn: proc (m: TMsg)
@@ -395,120 +395,6 @@ when useStackMaskHack:
     createThread(mainThread, tp)
     joinThread(mainThread)
 
-# --------------------------- lock handling ----------------------------------
-
-type
-  TLock* = TSysLock ## Nimrod lock; whether this is re-entrant
-                    ## or not is unspecified!
-  
-const
-  noDeadlocks = false # compileOption("deadlockPrevention")
-
-when false:
-  var
-    deadlocksPrevented*: int ## counts the number of times a 
-                             ## deadlock has been prevented
-    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 lock `lock`.
-  InitSysLock(lock)
-
-proc TryAcquire*(lock: var TLock): bool {.inline.} = 
-  ## Try to acquires the lock `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:
-      sysAssert 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)
-        sysAssert OrderedLocks()
-        return
-    # simply add to the end:
-    locks[locksLen] = p
-    inc(locksLen)
-    sysAssert OrderedLocks(g)
-
-proc Acquire*(lock: var TLock) =
-  ## Acquires the lock `lock`.
-  when nodeadlocks:
-    var p = addr(lock)
-    var L = locksLen-1
-    var i = 0
-    while i <= L:
-      sysAssert 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)
-        sysAssert OrderedLocks(g)
-        return
-        
-    # simply add to the end:
-    if locksLen >= len(locks):
-      raise newException(EResourceExhausted, "cannot acquire additional lock")
-    AcquireSys(lock)
-    locks[locksLen] = p
-    inc(locksLen)
-    sysAssert OrderedLocks(g)
-  else:
-    AcquireSys(lock)
-  
-proc Release*(lock: var TLock) =
-  ## Releases the lock `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)
-
 # ------------------------ message passing support ---------------------------
 
 proc getInBoxMem[TMsg](t: var TThread[TMsg]): pointer {.inline.} =
diff --git a/tests/accept/run/tnodeadlocks.nim b/tests/accept/run/tnodeadlocks.nim
index 3235e84ee..18fdca3e9 100755
--- a/tests/accept/run/tnodeadlocks.nim
+++ b/tests/accept/run/tnodeadlocks.nim
@@ -3,10 +3,10 @@ discard """
   cmd: "nimrod cc --hints:on --threads:on $# $#"
 """
 
-import os
+import os, locks
 
 const
-  noDeadlocks = defined(system.deadlocksPrevented)
+  noDeadlocks = defined(preventDeadlocks)
 
 var
   thr: array [0..5, TThread[tuple[a, b: int]]]
diff --git a/tests/threads/tthreadanalysis.nim b/tests/threads/tthreadanalysis.nim
index 84545ddf7..4edd51025 100755
--- a/tests/threads/tthreadanalysis.nim
+++ b/tests/threads/tthreadanalysis.nim
@@ -5,9 +5,6 @@ discard """
 
 import os
 
-const
-  noDeadlocks = defined(system.deadlocksPrevented)
-
 var
   thr: array [0..5, TThread[tuple[a, b: int]]]
 
diff --git a/tests/threads/tthreadanalysis2.nim b/tests/threads/tthreadanalysis2.nim
index 67e916c13..95f39723f 100755
--- a/tests/threads/tthreadanalysis2.nim
+++ b/tests/threads/tthreadanalysis2.nim
@@ -7,9 +7,6 @@ discard """
 
 import os
 
-const
-  noDeadlocks = defined(system.deadlocksPrevented)
-
 var
   thr: array [0..5, TThread[tuple[a, b: int]]]
 
diff --git a/todo.txt b/todo.txt
index 7efa74cae..b779eb6dc 100755
--- a/todo.txt
+++ b/todo.txt
@@ -3,9 +3,8 @@ Version 0.8.14
 
 - optional indentation for 'case' statement
 - test the sort implementation again
-- export re-entrant and non-reentrant locks and condition vars; threads should
-  not have an inbox per default
-  - add --deadlock_prevention:on|off switch? timeout for locks?
+- fix the 'const' issues
+- threads should not have an inbox per default
 - make threadvar efficient again on linux after testing
 
 
@@ -89,6 +88,7 @@ Low priority
 - code generated for type information is wasteful
 - resizing of strings/sequences could take into account the memory that
   is allocated
+- timeout for locks
 
 
 Version 2
diff --git a/web/news.txt b/web/news.txt
index 2dc3112a7..bb1bbec49 100755
--- a/web/news.txt
+++ b/web/news.txt
@@ -35,6 +35,7 @@ Changes affecting backwards compatibility
 - The ``is`` operator is now the ``of`` operator.
 - The ``is`` operator is now used to check type equivalence in generic code.
 - The ``pure`` pragma for procs has been renamed to ``noStackFrame``. 
+- The threading API has been completely redesigned.
 
 
 Language Additions
@@ -71,6 +72,7 @@ Library Additions
 - Added ``system.running`` for threads.
 - Added ``xmltree.innerText``.
 - Added ``os.isAbsolute``.
+- Added ``locks`` core module for more flexible locking support.
 
 
 2011-07-10 Version 0.8.12 released