fixes for exception handling; added system.compileOption
This commit is contained in:
parent
030d46f218
commit
866572e2e4
18 changed files with 329 additions and 165 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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) =
|
|||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue