diff options
Diffstat (limited to 'lib')
-rwxr-xr-x | lib/system.nim | 9 | ||||
-rwxr-xr-x | lib/system/excpt.nim | 185 | ||||
-rwxr-xr-x | lib/system/gc.nim | 3 | ||||
-rwxr-xr-x | lib/system/threads.nim | 66 |
4 files changed, 130 insertions, 133 deletions
diff --git a/lib/system.nim b/lib/system.nim index 1c8bf3ae9..7670288fc 100755 --- a/lib/system.nim +++ b/lib/system.nim @@ -1479,8 +1479,6 @@ when not defined(EcmaScript) and not defined(NimrodVM): strDesc.size = sizeof(string) strDesc.kind = tyString strDesc.flags = {ntfAcyclic} - initStackBottom() - initGC() # BUGFIX: need to be called here! include "system/ansi_c" @@ -1692,6 +1690,10 @@ when not defined(EcmaScript) and not defined(NimrodVM): when hasThreadSupport: include "system/threads" + else: + initStackBottom() + initGC() + include "system/excpt" # we cannot compile this with stack tracing on # as it would recurse endlessly! @@ -1755,8 +1757,7 @@ when not defined(EcmaScript) and not defined(NimrodVM): proc getCurrentException*(): ref E_Base {.compilerRtl, inl.} = ## retrieves the current exception; if there is none, nil is returned. - ThreadGlobals() - result = ||currException + result = currException proc getCurrentExceptionMsg*(): string {.inline.} = ## retrieves the error message that was attached to the current diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 75cac97ba..ac4ec2f0b 100755 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -10,6 +10,9 @@ # Exception handling code. This is difficult because it has # to work if there is no more memory (but it doesn't yet!). +# XXX assertions are unnecessarily complex; system should use their own +# assertion mechanism instead! + var stackTraceNewLine* = "\n" ## undocumented feature; it is replaced by ``<br>`` ## for CGI applications @@ -32,64 +35,51 @@ proc chckRange(i, a, b: int): int {.inline, compilerproc.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc.} proc chckNil(p: pointer) {.inline, compilerproc.} -when hasThreadSupport: - template ThreadGlobals = - var currentThread = ThisThread() - template `||`(varname: expr): expr = currentThread.g.varname - -else: - template ThreadGlobals = nil # nothing - template `||`(varname: expr): expr = varname - - var - framePtr: PFrame - excHandler: PSafePoint = nil - # list of exception handlers - # a global variable for the root of all try blocks - currException: ref E_Base - - buf: string # cannot be allocated on the stack! - assertBuf: string # we need a different buffer for - # assert, as it raises an exception and - # exception handler needs the buffer too - tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! - gAssertionFailed: ref EAssertionFailed - - new(||gAssertionFailed) - ||buf = newStringOfCap(2000) - ||assertBuf = newStringOfCap(2000) - +var + framePtr {.rtlThreadVar.}: PFrame + excHandler {.rtlThreadVar.}: PSafePoint + # list of exception handlers + # a global variable for the root of all try blocks + currException {.rtlThreadVar.}: ref E_Base + + buf {.rtlThreadVar.}: string # cannot be allocated on the stack! + assertBuf {.rtlThreadVar.}: string + # we need a different buffer for + # assert, as it raises an exception and + # exception handler needs the buffer too + gAssertionFailed {.rtlThreadVar.}: ref EAssertionFailed + +proc initGlobals() = + new(gAssertionFailed) + buf = newStringOfCap(2000) + assertBuf = newStringOfCap(2000) + +when not hasThreadSupport: + initGlobals() proc pushFrame(s: PFrame) {.compilerRtl, inl.} = - ThreadGlobals() - s.prev = ||framePtr - ||framePtr = s + s.prev = framePtr + framePtr = s proc popFrame {.compilerRtl, inl.} = - ThreadGlobals() - ||framePtr = (||framePtr).prev + framePtr = framePtr.prev proc setFrame(s: PFrame) {.compilerRtl, inl.} = - ThreadGlobals() - ||framePtr = s + framePtr = s proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} = - ThreadGlobals() - s.prev = ||excHandler - ||excHandler = s + s.prev = excHandler + excHandler = s proc popSafePoint {.compilerRtl, inl.} = - ThreadGlobals() - ||excHandler = (||excHandler).prev + excHandler = excHandler.prev proc pushCurrentException(e: ref E_Base) {.compilerRtl, inl.} = - ThreadGlobals() - e.parent = ||currException - ||currException = e + e.parent = currException + currException = e proc popCurrentException {.compilerRtl, inl.} = - ThreadGlobals() - ||currException = (||currException).parent + currException = currException.parent # some platforms have native support for stack traces: const @@ -143,18 +133,24 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: # Once we're past signalHandler, we're at what the user is # interested in enabled = true + +when not hasThreadSupport: + var + tempFrames: array [0..127, PFrame] # should not be alloc'd on stack proc auxWriteStackTrace(f: PFrame, s: var string) = - const + when hasThreadSupport: + var + tempFrames: array [0..127, PFrame] # but better than a threadvar + const firstCalls = 32 - ThreadGlobals() var it = f i = 0 total = 0 - while it != nil and i <= high(||tempFrames)-(firstCalls-1): + while it != nil and i <= high(tempFrames)-(firstCalls-1): # the (-1) is for a nil entry that marks where the '...' should occur - (||tempFrames)[i] = it + tempFrames[i] = it inc(i) inc(total) it = it.prev @@ -165,38 +161,37 @@ proc auxWriteStackTrace(f: PFrame, s: var string) = for j in 1..total-i-(firstCalls-1): if b != nil: b = b.prev if total != i: - (||tempFrames)[i] = nil + tempFrames[i] = nil inc(i) - while b != nil and i <= high(||tempFrames): - (||tempFrames)[i] = b + while b != nil and i <= high(tempFrames): + tempFrames[i] = b inc(i) b = b.prev for j in countdown(i-1, 0): - if (||tempFrames)[j] == nil: + if tempFrames[j] == nil: add(s, "(") add(s, $(total-i-1)) add(s, " calls omitted) ...") else: var oldLen = s.len - add(s, (||tempFrames)[j].filename) - if (||tempFrames)[j].line > 0: + add(s, tempFrames[j].filename) + if tempFrames[j].line > 0: add(s, '(') - add(s, $(||tempFrames)[j].line) + add(s, $tempFrames[j].line) add(s, ')') for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') - add(s, (||tempFrames)[j].procname) + add(s, tempFrames[j].procname) add(s, stackTraceNewLine) proc rawWriteStackTrace(s: var string) = when nimrodStackTrace: - ThreadGlobals() - if ||framePtr == nil: + 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) + auxWriteStackTrace(framePtr, s) elif defined(nativeStackTrace) and nativeStackTraceSupported: add(s, "Traceback from system (most recent call last)") add(s, stackTraceNewLine) @@ -216,53 +211,50 @@ proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = if raiseHook != nil: if not raiseHook(e): return GC_disable() # a bad thing is an error in the GC while raising an exception - ThreadGlobals() - if ||excHandler != nil: + if excHandler != nil: pushCurrentException(e) - c_longjmp((||excHandler).context, 1) + c_longjmp(excHandler.context, 1) else: - if not isNil(||buf): - setLen(||buf, 0) - rawWriteStackTrace(||buf) + if not isNil(buf): + setLen(buf, 0) + rawWriteStackTrace(buf) if e.msg != nil and e.msg[0] != '\0': - add(||buf, "Error: unhandled exception: ") - add(||buf, $e.msg) + add(buf, "Error: unhandled exception: ") + add(buf, $e.msg) else: - add(||buf, "Error: unhandled exception") - add(||buf, " [") - add(||buf, $ename) - add(||buf, "]\n") - writeToStdErr(||buf) + add(buf, "Error: unhandled exception") + add(buf, " [") + add(buf, $ename) + add(buf, "]\n") + writeToStdErr(buf) else: writeToStdErr(ename) quitOrDebug() GC_enable() proc reraiseException() {.compilerRtl.} = - ThreadGlobals() - if ||currException == nil: + if currException == nil: raise newException(ENoExceptionToReraise, "no exception to reraise") else: - raiseException(||currException, (||currException).name) + raiseException(currException, currException.name) proc internalAssert(file: cstring, line: int, cond: bool) {.compilerproc.} = if not cond: - ThreadGlobals() #c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) #quit(1) GC_disable() # BUGFIX: `$` allocates a new string object! - if not isNil(||assertBuf): + if not isNil(assertBuf): # BUGFIX: when debugging the GC, assertBuf may be nil - setLen(||assertBuf, 0) - add(||assertBuf, "[Assertion failure] file: ") - add(||assertBuf, file) - add(||assertBuf, " line: ") - add(||assertBuf, $line) - add(||assertBuf, "\n") - (||gAssertionFailed).msg = ||assertBuf + setLen(assertBuf, 0) + add(assertBuf, "[Assertion failure] file: ") + add(assertBuf, file) + add(assertBuf, " line: ") + add(assertBuf, $line) + add(assertBuf, "\n") + gAssertionFailed.msg = assertBuf GC_enable() - if ||gAssertionFailed != nil: - raise ||gAssertionFailed + if gAssertionFailed != nil: + raise gAssertionFailed else: c_fprintf(c_stdout, "Assertion failure: file %s line %ld\n", file, line) quit(1) @@ -277,24 +269,23 @@ var proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = # print stack trace and quit - ThreadGlobals() var s = sig GC_disable() - setLen(||buf, 0) - rawWriteStackTrace(||buf) + setLen(buf, 0) + rawWriteStackTrace(buf) - if s == SIGINT: add(||buf, "SIGINT: Interrupted by Ctrl-C.\n") + if s == SIGINT: add(buf, "SIGINT: Interrupted by Ctrl-C.\n") elif s == SIGSEGV: - add(||buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") + add(buf, "SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") elif s == SIGABRT: if dbgAborting: return # the debugger wants to abort - add(||buf, "SIGABRT: Abnormal termination.\n") - elif s == SIGFPE: add(||buf, "SIGFPE: Arithmetic error.\n") - elif s == SIGILL: add(||buf, "SIGILL: Illegal operation.\n") + add(buf, "SIGABRT: Abnormal termination.\n") + elif s == SIGFPE: add(buf, "SIGFPE: Arithmetic error.\n") + elif s == SIGILL: add(buf, "SIGILL: Illegal operation.\n") elif s == SIGBUS: - add(||buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") - else: add(||buf, "unknown signal\n") - writeToStdErr(||buf) + add(buf, "SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") + else: add(buf, "unknown signal\n") + writeToStdErr(buf) dbgAborting = True # play safe here... GC_enable() quit(1) # always quit when SIGABRT diff --git a/lib/system/gc.nim b/lib/system/gc.nim index d2a6b4b94..033a7bdbe 100755 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -63,7 +63,7 @@ type var stackBottom {.rtlThreadVar.}: pointer gch {.rtlThreadVar.}: TGcHeap - cycleThreshold {.rtlThreadVar.}: int = InitialCycleThreshold + cycleThreshold {.rtlThreadVar.}: int proc acquire(gch: var TGcHeap) {.inline.} = when hasThreadSupport and hasSharedHeap: @@ -267,6 +267,7 @@ proc initGC() = when not defined(useNimRtl): when traceGC: for i in low(TCellState)..high(TCellState): Init(states[i]) + cycleThreshold = InitialCycleThreshold gch.stat.stackScans = 0 gch.stat.cycleCollections = 0 gch.stat.maxThreshold = 0 diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 7b035e819..db16502ff 100755 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -52,22 +52,22 @@ when defined(Windows): LockSemaphore: int Reserved: int32 - proc InitSysLock(L: var TSysLock) {.stdcall, + proc InitSysLock(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeCriticalSection".} ## Initializes the lock `L`. - proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, + proc TryAcquireSysAux(L: var TSysLock): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "TryEnterCriticalSection".} ## Tries to acquire the lock `L`. proc TryAcquireSys(L: var TSysLock): bool {.inline.} = result = TryAcquireSysAux(L) != 0'i32 - proc AcquireSys(L: var TSysLock) {.stdcall, + proc AcquireSys(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "EnterCriticalSection".} ## Acquires the lock `L`. - proc ReleaseSys(L: var TSysLock) {.stdcall, + proc ReleaseSys(L: var TSysLock) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "LeaveCriticalSection".} ## Releases the lock `L`. @@ -120,17 +120,17 @@ else: header: "<sys/types.h>".} = object proc InitSysLock(L: var TSysLock, attr: pointer = nil) {. - importc: "pthread_mutex_init", header: "<pthread.h>".} + importc: "pthread_mutex_init", header: "<pthread.h>", noSideEffect.} - proc AcquireSys(L: var TSysLock) {. + proc AcquireSys(L: var TSysLock) {.noSideEffect, importc: "pthread_mutex_lock", header: "<pthread.h>".} - proc TryAcquireSysAux(L: var TSysLock): cint {. + proc TryAcquireSysAux(L: var TSysLock): cint {.noSideEffect, importc: "pthread_mutex_trylock", header: "<pthread.h>".} proc TryAcquireSys(L: var TSysLock): bool {.inline.} = result = TryAcquireSysAux(L) == 0'i32 - proc ReleaseSys(L: var TSysLock) {. + proc ReleaseSys(L: var TSysLock) {.noSideEffect, importc: "pthread_mutex_unlock", header: "<pthread.h>".} type @@ -184,32 +184,25 @@ else: proc pthread_setspecific(a1: TThreadVarSlot, a2: pointer): int32 {. importc: "pthread_setspecific", header: "<pthread.h>".} - proc ThreadVarAlloc(): TThreadVarSlot {.compilerproc, inline.} = + proc ThreadVarAlloc(): TThreadVarSlot {.inline.} = discard pthread_key_create(addr(result), nil) - proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {. - compilerproc, inline.} = + proc ThreadVarSetValue(s: TThreadVarSlot, value: pointer) {.inline.} = discard pthread_setspecific(s, value) - proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.compilerproc, inline.} = + proc ThreadVarGetValue(s: TThreadVarSlot): pointer {.inline.} = result = pthread_getspecific(s) -type - TGlobals {.final, pure.} = object - excHandler: PSafePoint - currException: ref E_Base - framePtr: PFrame - buf: string # cannot be allocated on the stack! - assertBuf: string # we need a different buffer for - # assert, as it raises an exception and - # exception handler needs the buffer too - gAssertionFailed: ref EAssertionFailed - tempFrames: array [0..127, PFrame] # cannot be allocated on the stack! - data: float # compiler should add thread local variables here! - -proc initGlobals(g: var TGlobals) = - new(g.gAssertionFailed) - g.buf = newStringOfCap(2000) - g.assertBuf = newStringOfCap(2000) +const emulatedThreadVars = defined(macosx) + +when emulatedThreadVars: + # the compiler generates this proc for us, so that we can get the size of + # the thread local var block: + proc NimThreadVarsSize(): int {.noconv, importc: "NimThreadVarsSize".} +proc ThreadVarsAlloc(size: int): pointer = + result = c_malloc(size) + zeroMem(result, size) +proc ThreadVarsDealloc(p: pointer) {.importc: "free", nodecl.} +proc initGlobals() type PGcThread = ptr TGcThread @@ -218,7 +211,6 @@ type next, prev: PGcThread stackBottom, stackTop, threadLocalStorage: pointer stackSize: int - g: TGlobals locksLen: int locks: array [0..MaxLocksPerThread-1, pointer] registers: array[0..maxRegisters-1, pointer] # register contents for GC @@ -240,8 +232,14 @@ proc GetThreadLocalVars(): pointer {.compilerRtl, inl.} = # of all threads; it's not to be stopped etc. when not defined(useNimRtl): var mainThread: TGcThread - initGlobals(mainThread.g) + ThreadVarSetValue(globalsSlot, addr(mainThread)) + when emulatedThreadVars: + mainThread.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize()) + + initStackBottom() + initGC() + initGlobals() var heapLock: TSysLock InitSysLock(HeapLock) @@ -296,15 +294,21 @@ when not defined(boehmgc) and not hasSharedHeap: template ThreadProcWrapperBody(closure: expr) = ThreadVarSetValue(globalsSlot, closure) var t = cast[ptr TThread[TParam]](closure) + when emulatedThreadVars: + t.threadLocalStorage = ThreadVarsAlloc(NimThreadVarsSize()) when not defined(boehmgc) and not hasSharedHeap: # init the GC for this thread: setStackBottom(addr(t)) initGC() t.stackBottom = addr(t) + initGlobals() registerThread(t) try: t.fn(t.data) finally: + # XXX shut-down is not executed when the thread is forced down! + when emulatedThreadVars: + ThreadVarsDealloc(t.threadLocalStorage) unregisterThread(t) when defined(deallocOsPages): deallocOsPages() |