diff options
author | Araq <rumpf_a@web.de> | 2010-09-13 00:52:44 +0200 |
---|---|---|
committer | Araq <rumpf_a@web.de> | 2010-09-13 00:52:44 +0200 |
commit | 866572e2e4601a1248e5ac78b151dc48fb483aa4 (patch) | |
tree | f38b3574eb66ed398d176175564eeb264bd96376 /lib | |
parent | 030d46f21804d8dd82edf7d5d2875e8f034dd86a (diff) | |
download | Nim-866572e2e4601a1248e5ac78b151dc48fb483aa4.tar.gz |
fixes for exception handling; added system.compileOption
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/nimbase.h | 12 | ||||
-rwxr-xr-x | lib/system.nim | 57 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 48 | ||||
-rwxr-xr-x | lib/system/gc.nim | 45 | ||||
-rwxr-xr-x | lib/system/systhread.nim | 46 |
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) = - |