summary refs log tree commit diff stats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rwxr-xr-xlib/system.nim9
-rwxr-xr-xlib/system/excpt.nim185
-rwxr-xr-xlib/system/gc.nim3
-rwxr-xr-xlib/system/threads.nim66
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()