fixes for exception handling; added system.compileOption

This commit is contained in:
Araq 2010-09-13 00:52:44 +02:00
commit 866572e2e4
18 changed files with 329 additions and 165 deletions

View file

@ -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

View file

@ -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):

View file

@ -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:
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, "No stack traceback available")
add(s, stackTraceNewLine)
else:
add(s, "Traceback (most recent call last)")
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

View file

@ -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()

View file

@ -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) =