summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2010-09-13 00:52:44 +0200
committerAraq <rumpf_a@web.de>2010-09-13 00:52:44 +0200
commit866572e2e4601a1248e5ac78b151dc48fb483aa4 (patch)
treef38b3574eb66ed398d176175564eeb264bd96376 /lib
parent030d46f21804d8dd82edf7d5d2875e8f034dd86a (diff)
downloadNim-866572e2e4601a1248e5ac78b151dc48fb483aa4.tar.gz
fixes for exception handling; added system.compileOption
Diffstat (limited to 'lib')
-rwxr-xr-xlib/nimbase.h12
-rwxr-xr-xlib/system.nim57
-rwxr-xr-xlib/system/excpt.nim48
-rwxr-xr-xlib/system/gc.nim45
-rwxr-xr-xlib/system/systhread.nim46
5 files changed, 151 insertions, 57 deletions
diff --git a/lib/nimbase.h b/lib/nimbase.h
index 01c3ece20..983bb112d 100755
--- a/lib/nimbase.h
+++ b/lib/nimbase.h
@@ -177,10 +177,6 @@ __TINYC__
 **
 **  Fortunately the ISO C99 specifications define the functions lrint, lrintf,
 **  llrint and llrintf which fix this problem as a side effect.
-**
-**  On Unix-like systems, the configure process should have detected the
-**  presence of these functions. If they weren't found we have to replace them
-**  here with a standard C cast.
 */
 
 /*
@@ -444,4 +440,12 @@ __declspec(naked) int __fastcall NimXadd(volatile int* pNum, int val) {
 }
 #endif
 
+#ifdef __GNUC__
+#  define likely(x) __builtin_expect(x, 1)
+#  define unlikely(x) __builtin_expect(x, 0)
+#else
+#  define likely(x) (x)
+#  define unlikely(x) (x)
+#endif
+
 #endif
diff --git a/lib/system.nim b/lib/system.nim
index 0152ebf23..0b2959266 100755
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -144,6 +144,7 @@ type
   E_Base* {.compilerproc.} = object of TObject ## base exception class;
                                                ## each exception has to
                                                ## inherit from `E_Base`.
+    parent: ref E_Base        ## parent exception (can be used as a stack)
     name: cstring             ## The exception's name is its Nimrod identifier.
                               ## This field is filled automatically in the
                               ## ``raise`` statement.
@@ -717,6 +718,22 @@ const
     ## a string that describes the application type. Possible values:
     ## "console", "gui", "lib".
   
+proc compileOption*(option: string): bool {.
+  magic: "CompileOption", noSideEffect.}
+  ## can be used to determine a on|off compile-time option. Example:
+  ##
+  ## .. code-block:: nimrod
+  ## when compileOption("floatchecks"): 
+  ##   echo "compiled with floating point NaN and Inf checks"
+  
+proc compileOption*(option, arg: string): bool {.
+  magic: "CompileOptionArg", noSideEffect.}
+  ## can be used to determine an enum compile-time option. Example:
+  ##
+  ## .. code-block:: nimrod
+  ## when compileOption("opt", "size") and compileOption("gc", "boehm"): 
+  ##   echo "compiled with optimization for size and uses Boehm's GC"
+  
 include "system/inclrtl"
 include "system/cgprocs"
 
@@ -961,14 +978,6 @@ proc getRefcount*[T](x: ref T): int {.importc: "getRefcount", noSideEffect.}
 
 #proc writeStackTrace() {.export: "writeStackTrace".}
 
-when not defined(NimrodVM):
-  proc getCurrentExceptionMsg*(): string {.exportc.}
-    ## retrieves the error message that was attached to the current
-    ## exception; if there is none, "" is returned.
-  
-  proc getCurrentException*(): ref E_Base
-    ## retrieves the current exception; if there is none, nil is returned.
-
 # new constants:
 const
   inf* {.magic: "Inf".} = 1.0 / 0.0
@@ -1168,15 +1177,6 @@ proc each*[T](data: var openArray[T], op: proc (x: var T)) =
   ## `op` to every item in `data`.
   for i in 0..data.len-1: op(data[i])
 
-
-# ----------------- FPU ------------------------------------------------------
-
-#proc disableFPUExceptions*()
-# disables all floating point unit exceptions
-
-#proc enableFPUExceptions*()
-# enables all floating point unit exceptions
-
 # ----------------- GC interface ---------------------------------------------
 
 proc GC_disable*() {.rtl.}
@@ -1577,14 +1577,15 @@ when not defined(EcmaScript) and not defined(NimrodVM):
   include "system/assign"
   include "system/repr"
 
-  # we have to implement it here after gentostr for the cstrToNimStrDummy proc
-  proc getCurrentExceptionMsg(): string =
-    if excHandler == nil: return ""
-    return $excHandler.exc.msg
+  proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} =
+    ## retrieves the current exception; if there is none, nil is returned.
+    result = currException
 
-  proc getCurrentException(): ref E_Base = 
-    if excHandler != nil: 
-      result = excHandler.exc
+  proc getCurrentExceptionMsg*(): string {.inline.} =
+    ## retrieves the error message that was attached to the current
+    ## exception; if there is none, "" is returned.
+    var e = getCurrentException()
+    return if e == nil: "" else: e.msg
 
   {.push stack_trace: off.}
   when defined(endb):
@@ -1594,6 +1595,14 @@ when not defined(EcmaScript) and not defined(NimrodVM):
     include "system/profiler"
   {.pop.} # stacktrace
 
+  proc likely*(val: bool): bool {.importc: "likely", nodecl, nosideeffect.}
+    ## can be used to mark a condition to be likely. This is a hint for the 
+    ## optimizer.
+  
+  proc unlikely*(val: bool): bool {.importc: "unlikely", nodecl, nosideeffect.}
+    ## can be used to mark a condition to be unlikely. This is a hint for the 
+    ## optimizer.
+
 elif defined(ecmaScript):
   include "system/ecmasys"
 elif defined(NimrodVM):
diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim
index d8bdf2a9f..c473c42f0 100755
--- a/lib/system/excpt.nim
+++ b/lib/system/excpt.nim
@@ -20,9 +20,6 @@ else:
   proc writeToStdErr(msg: CString) =
     discard MessageBoxA(0, msg, nil, 0)
 
-proc raiseException(e: ref E_Base, ename: CString) {.compilerproc.}
-proc reraiseException() {.compilerproc.}
-
 proc registerSignalHandler() {.compilerproc.}
 
 proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
@@ -34,20 +31,29 @@ type
   PSafePoint = ptr TSafePoint
   TSafePoint {.compilerproc, final.} = object
     prev: PSafePoint # points to next safe point ON THE STACK
-    exc: ref E_Base
     status: int
+    exc: ref E_Base  # XXX only needed for bootstrapping
     context: C_JmpBuf
 
 var
   excHandler {.compilerproc.}: PSafePoint = nil
     # list of exception handlers
     # a global variable for the root of all try blocks
+  currException: ref E_Base
 
-proc reraiseException() =
-  if excHandler == nil:
-    raise newException(ENoExceptionToReraise, "no exception to reraise")
-  else:
-    c_longjmp(excHandler.context, 1)
+proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = 
+  s.prev = excHandler
+  excHandler = s
+
+proc popSafePoint {.compilerRtl, inl.} =
+  excHandler = excHandler.prev
+
+proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} = 
+  e.parent = currException
+  currException = e
+
+proc popCurrentException {.compilerRtl, inl.} =
+  currException = currException.parent
 
 type
   PFrame = ptr TFrame
@@ -114,13 +120,17 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
     add(s, stackTraceNewLine)
 
 proc rawWriteStackTrace(s: var string) =
-  if framePtr == nil:
-    add(s, "No stack traceback available")
-    add(s, stackTraceNewLine)
+  when compileOption("stacktrace") or compileOption("linetrace"):
+    if framePtr == nil:
+      add(s, "No stack traceback available")
+      add(s, stackTraceNewLine)
+    else:
+      add(s, "Traceback (most recent call last)")
+      add(s, stackTraceNewLine)
+      auxWriteStackTrace(framePtr, s)
   else:
-    add(s, "Traceback (most recent call last)")
+    add(s, "No stack traceback available")
     add(s, stackTraceNewLine)
-    auxWriteStackTrace(framePtr, s)
 
 proc quitOrDebug() {.inline.} =
   when not defined(endb):
@@ -128,11 +138,11 @@ proc quitOrDebug() {.inline.} =
   else:
     endbStep() # call the debugger
 
-proc raiseException(e: ref E_Base, ename: CString) =
+proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
   GC_disable() # a bad thing is an error in the GC while raising an exception
   e.name = ename
   if excHandler != nil:
-    excHandler.exc = e
+    pushCurrentException(e)
     c_longjmp(excHandler.context, 1)
   else:
     if not isNil(buf):
@@ -152,6 +162,12 @@ proc raiseException(e: ref E_Base, ename: CString) =
     quitOrDebug()
   GC_enable()
 
+proc reraiseException() {.compilerRtl.} =
+  if currException == nil:
+    raise newException(ENoExceptionToReraise, "no exception to reraise")
+  else:
+    raiseException(currException, currException.name)
+
 var
   gAssertionFailed: ref EAssertionFailed
 
diff --git a/lib/system/gc.nim b/lib/system/gc.nim
index cd803d70a..0c403b4bc 100755
--- a/lib/system/gc.nim
+++ b/lib/system/gc.nim
@@ -61,6 +61,8 @@ type
     decStack: TCellSeq       # cells in the stack that are to decref again
     cycleRoots: TCellSet
     tempStack: TCellSeq      # temporary stack for recursion elimination
+    cycleRootsLock: TSysLock
+    zctLock: TSysLock
     stat: TGcStat
 
 var
@@ -68,12 +70,22 @@ var
   gch: TGcHeap
   cycleThreshold: int = InitialCycleThreshold
   recGcLock: int = 0
-    # we use a lock to prevend the garbage collector to be triggered in a
+    # we use a lock to prevent the garbage collector to be triggered in a
     # finalizer; the collector should not call itself this way! Thus every
     # object allocated by a finalizer will not trigger a garbage collection.
     # This is wasteful but safe. This is a lock against recursive garbage
     # collection, not a lock for threads!
 
+proc lock(gch: var TGcHeap) {.inline.} = 
+  if isMultiThreaded: 
+    Lock(gch.zctLock)
+    lock(gch.cycleRootsLock)
+
+proc unlock(gch: var TGcHeap) {.inline.} = 
+  if isMultiThreaded: 
+    unlock(gch.zctLock)
+    unlock(gch.cycleRootsLock)
+
 proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
   if (c.refcount and rcZct) == 0:
     c.refcount = c.refcount and not colorMask or rcZct
@@ -159,7 +171,7 @@ when traceGC:
     for c in elements(states[csAllocated]):
       inc(e)
       if c in states[csZctFreed]: inc(z)
-      elif c in states[csCycFreed]: inc(z)
+      elif c in states[csCycFreed]: inc(y)
       else: writeCell("leak", c)
     cfprintf(cstdout, "Allocations: %ld; ZCT freed: %ld; CYC freed: %ld\n",
              e, z, y)
@@ -190,25 +202,28 @@ proc prepareDealloc(cell: PCell) =
 
 proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = 
   # we MUST access gch as a global here, because this crosses DLL boundaries!
+  if isMultiThreaded: Lock(gch.cycleRootsLock)
   incl(gch.cycleRoots, c)
+  if isMultiThreaded: Unlock(gch.cycleRootsLock)
 
 proc rtlAddZCT(c: PCell) {.rtl, inl.} =
   # we MUST access gch as a global here, because this crosses DLL boundaries!
+  if isMultiThreaded: Lock(gch.zctLock)
   addZCT(gch.zct, c)
+  if isMultiThreaded: Unlock(gch.zctLock)
 
 proc decRef(c: PCell) {.inline.} =
   when stressGC:
     if c.refcount <% rcIncrement:
       writeCell("broken cell", c)
   assert(c.refcount >=% rcIncrement)
-  c.refcount = c.refcount -% rcIncrement
-  if c.refcount <% rcIncrement:
+  if atomicDec(c.refcount, rcIncrement) <% rcIncrement:
     rtlAddZCT(c)
   elif canBeCycleRoot(c):
     rtlAddCycleRoot(c) 
 
 proc incRef(c: PCell) {.inline.} = 
-  c.refcount = c.refcount +% rcIncrement
+  discard atomicInc(c.refcount, rcIncrement)
   if canBeCycleRoot(c):
     rtlAddCycleRoot(c)
 
@@ -228,11 +243,10 @@ proc asgnRefNoCycle(dest: ppointer, src: pointer) {.compilerProc, inline.} =
   # cycle is possible.
   if src != nil: 
     var c = usrToCell(src)
-    c.refcount = c.refcount +% rcIncrement
+    discard atomicInc(c.refcount, rcIncrement)
   if dest^ != nil: 
     var c = usrToCell(dest^)
-    c.refcount = c.refcount -% rcIncrement
-    if c.refcount <% rcIncrement:
+    if atomicDec(c.refcount, rcIncrement) <% rcIncrement:
       rtlAddZCT(c)
   dest^ = src
 
@@ -260,6 +274,8 @@ proc initGC() =
     init(gch.tempStack)
     Init(gch.cycleRoots)
     Init(gch.decStack)
+    InitLock(gch.cycleRootsLock)
+    InitLock(gch.zctLock)
     new(gOutOfMem) # reserve space for the EOutOfMemory exception here!
 
 proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
@@ -310,6 +326,7 @@ proc checkCollection {.inline.} =
 
 proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
   # generates a new object and sets its reference counter to 0
+  lock(gch)
   assert(typ.kind in {tyRef, tyString, tySequence})
   checkCollection()
   var res = cast[PCell](rawAlloc(allocator, size + sizeof(TCell)))
@@ -337,15 +354,18 @@ proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
         break addToZCT
     add(gch.zct, res)
   when logGC: writeCell("new cell", res)
-  gcTrace(res, csAllocated)
+  gcTrace(res, csAllocated)  
+  unlock(gch)
   result = cellToUsr(res)
 
 proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
+  # `newObj` already uses locks, so no need for them here.
   result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
   cast[PGenericSeq](result).len = len
   cast[PGenericSeq](result).space = len
 
 proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
+  lock(gch)
   checkCollection()
   var ol = usrToCell(old)
   assert(ol.typ != nil)
@@ -383,6 +403,7 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
   else:
     assert(ol.typ != nil)
     zeroMem(ol, sizeof(TCell))
+  unlock(gch)
   result = cellToUsr(res)
 
 # ---------------- cycle collector -------------------------------------------
@@ -632,9 +653,9 @@ proc collectCT(gch: var TGcHeap) =
     unmarkStackAndRegisters(gch)
 
 when not defined(useNimRtl):
-  proc GC_disable() = inc(recGcLock)
+  proc GC_disable() = discard atomicInc(recGcLock, 1)
   proc GC_enable() =
-    if recGcLock > 0: dec(recGcLock)
+    if recGcLock > 0: discard atomicDec(recGcLock, 1)
 
   proc GC_setStrategy(strategy: TGC_Strategy) =
     case strategy
@@ -651,10 +672,12 @@ when not defined(useNimRtl):
     # set to the max value to suppress the cycle detector
 
   proc GC_fullCollect() =
+    lock(gch)
     var oldThreshold = cycleThreshold
     cycleThreshold = 0 # forces cycle collection
     collectCT(gch)
     cycleThreshold = oldThreshold
+    unlock(gch)
 
   proc GC_getStatistics(): string =
     GC_disable()
diff --git a/lib/system/systhread.nim b/lib/system/systhread.nim
index 58482ac65..583cd2a43 100755
--- a/lib/system/systhread.nim
+++ b/lib/system/systhread.nim
@@ -15,6 +15,10 @@ when defined(gcc) or defined(llvm_gcc):
 elif defined(vcc):
   proc sync_add_and_fetch(p: var int, val: int): int {.
     importc: "NimXadd", nodecl.}
+else:
+  proc sync_add_and_fetch(p: var int, val: int): int {.inline.} =
+    inc(p, val)
+    result = p
 
 const
   isMultiThreaded* = true
@@ -37,12 +41,51 @@ proc atomicDec(memLoc: var int, x: int): int =
     dec(memLoc, x)
     result = memLoc  
   
+when defined(Windows):
+  type 
+    THandle = int
+    TSysLock {.final, pure.} = object # CRITICAL_SECTION in WinApi
+      DebugInfo: pointer
+      LockCount: int32
+      RecursionCount: int32
+      OwningThread: int
+      LockSemaphore: int
+      Reserved: int32
+  
+  proc InitLock(L: var TSysLock) {.stdcall,
+    dynlib: "kernel32", importc: "InitializeCriticalSection".}
+  proc Lock(L: var TSysLock) {.stdcall,
+    dynlib: "kernel32", importc: "EnterCriticalSection".}
+  proc Unlock(L: var TSysLock) {.stdcall,
+    dynlib: "kernel32", importc: "LeaveCriticalSection".}
+
+  proc CreateThread(lpThreadAttributes: Pointer, dwStackSize: int32,
+                     lpStartAddress: pointer, lpParameter: Pointer,
+                     dwCreationFlags: int32, lpThreadId: var int32): THandle {.
+    stdcall, dynlib: "kernel32", importc: "CreateThread".}
+
+  
+else:
+  type
+    TSysLock {.importc: "pthread_mutex_t", header: "<sys/types.h>".} = int
+    TSysThread {.importc: "pthread_t", header: "<sys/types.h>".} = int
+
+  proc InitLock(L: var TSysLock, attr: pointer = nil) {.
+    importc: "pthread_mutex_init", header: "<pthread.h>".}
+  proc Lock(L: var TSysLock) {.
+    importc: "pthread_mutex_lock", header: "<pthread.h>".}
+  proc Unlock(L: var TSysLock) {.
+    importc: "pthread_mutex_unlock", header: "<pthread.h>".}
+  
+  
 type
   TThread* {.final, pure.} = object
+    id: int
     next: ptr TThread
-  TThreadFunc* = proc (closure: pointer)
+  TThreadFunc* = proc (closure: pointer) {.cdecl.}
   
 proc createThread*(t: var TThread, fn: TThreadFunc) = 
+  
   nil
   
 proc destroyThread*(t: var TThread) =
@@ -50,4 +93,3 @@ proc destroyThread*(t: var TThread) =
 
 
 
-