diff options
Diffstat (limited to 'lib/system')
-rw-r--r-- | lib/system/assign.nim | 2 | ||||
-rw-r--r-- | lib/system/channels.nim | 4 | ||||
-rw-r--r-- | lib/system/excpt.nim | 80 | ||||
-rw-r--r-- | lib/system/gc.nim | 136 | ||||
-rw-r--r-- | lib/system/mmdisp.nim | 2 | ||||
-rw-r--r-- | lib/system/repr.nim | 29 |
6 files changed, 178 insertions, 75 deletions
diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 19d4ebf57..3b43abcd1 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -73,6 +73,8 @@ proc genericAssignAux(dest, src: Pointer, mt: PNimType, shallow: bool) = # sequence reallocations: var pint = cast[ptr PNimType](dest) pint[] = cast[ptr PNimType](src)[] + if mt.base != nil: + genericAssignAux(dest, src, mt.base, shallow) genericAssignAux(dest, src, mt.node, shallow) of tyTuple: genericAssignAux(dest, src, mt.node, shallow) diff --git a/lib/system/channels.nim b/lib/system/channels.nim index f5bcdec03..d0294322a 100644 --- a/lib/system/channels.nim +++ b/lib/system/channels.nim @@ -120,7 +120,9 @@ proc storeAux(dest, src: Pointer, mt: PNimType, t: PRawChannel, # copy type field: var pint = cast[ptr PNimType](dest) # XXX use dynamic type here! - pint[] = mt + pint[] = mt + if mt.base != nil: + storeAux(dest, src, mt.base, t, mode) storeAux(dest, src, mt.node, t, mode) of tyTuple: storeAux(dest, src, mt.node, t, mode) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 03200c8e2..7937d9738 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -27,8 +27,6 @@ else: proc writeToStdErr(msg: CString) = discard MessageBoxA(0, msg, nil, 0) -proc registerSignalHandler() - proc chckIndx(i, a, b: int): int {.inline, compilerproc.} proc chckRange(i, a, b: int): int {.inline, compilerproc.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc.} @@ -278,46 +276,46 @@ when defined(endb): var dbgAborting: bool # whether the debugger wants to abort -proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = - template processSignal(s, action: expr) {.immediate.} = - if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") - elif s == SIGSEGV: - action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") - elif s == SIGABRT: - when defined(endb): - if dbgAborting: return # the debugger wants to abort - action("SIGABRT: Abnormal termination.\n") - elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n") - elif s == SIGILL: action("SIGILL: Illegal operation.\n") - elif s == SIGBUS: - action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") - else: action("unknown signal\n") - - # print stack trace and quit - when hasSomeStackTrace: - GC_disable() - var buf = newStringOfCap(2000) - rawWriteStackTrace(buf) - processSignal(sig, buf.add) # nice hu? currying a la nimrod :-) - writeToStdErr(buf) - GC_enable() - else: - var msg: cstring - template asgn(y: expr) = msg = y - processSignal(sig, asgn) - writeToStdErr(msg) - when defined(endb): dbgAborting = True - quit(1) # always quit when SIGABRT - -proc registerSignalHandler() = - c_signal(SIGINT, signalHandler) - c_signal(SIGSEGV, signalHandler) - c_signal(SIGABRT, signalHandler) - c_signal(SIGFPE, signalHandler) - c_signal(SIGILL, signalHandler) - c_signal(SIGBUS, signalHandler) - when not defined(noSignalHandler): + proc signalHandler(sig: cint) {.exportc: "signalHandler", noconv.} = + template processSignal(s, action: expr) {.immediate.} = + if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") + elif s == SIGSEGV: + action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n") + elif s == SIGABRT: + when defined(endb): + if dbgAborting: return # the debugger wants to abort + action("SIGABRT: Abnormal termination.\n") + elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n") + elif s == SIGILL: action("SIGILL: Illegal operation.\n") + elif s == SIGBUS: + action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n") + else: action("unknown signal\n") + + # print stack trace and quit + when hasSomeStackTrace: + GC_disable() + var buf = newStringOfCap(2000) + rawWriteStackTrace(buf) + processSignal(sig, buf.add) # nice hu? currying a la nimrod :-) + writeToStdErr(buf) + GC_enable() + else: + var msg: cstring + template asgn(y: expr) = msg = y + processSignal(sig, asgn) + writeToStdErr(msg) + when defined(endb): dbgAborting = True + quit(1) # always quit when SIGABRT + + proc registerSignalHandler() = + c_signal(SIGINT, signalHandler) + c_signal(SIGSEGV, signalHandler) + c_signal(SIGABRT, signalHandler) + c_signal(SIGFPE, signalHandler) + c_signal(SIGILL, signalHandler) + c_signal(SIGBUS, signalHandler) + registerSignalHandler() # call it in initialization section proc setControlCHook(hook: proc () {.noconv.}) = diff --git a/lib/system/gc.nim b/lib/system/gc.nim index f5b68b9db..c5d4d2aa2 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -25,6 +25,10 @@ const # reaches this threshold # this seems to be a good value withRealTime = defined(useRealtimeGC) + useMarkForDebug = defined(gcGenerational) + useBackupGc = false # use a simple M&S GC to collect + # cycles instead of the complex + # algorithm when withRealTime and not defined(getTicks): include "system/timers" @@ -42,8 +46,10 @@ const colorMask = 0b011 type TWalkOp = enum + waMarkGlobal, # part of the backup/debug mark&sweep + waMarkPrecise, # part of the backup/debug mark&sweep waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, - waCollectWhite + waCollectWhite, TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.} # A ref type can have a finalizer that is called before the object's @@ -71,6 +77,8 @@ type maxPause: TNanos # max allowed pause in nanoseconds; active if > 0 region: TMemRegion # garbage collected region stat: TGcStat + when useMarkForDebug or useBackupGc: + marked: TCellSet var gch {.rtlThreadVar.}: TGcHeap @@ -80,7 +88,7 @@ when not defined(useNimRtl): template acquire(gch: TGcHeap) = when hasThreadSupport and hasSharedHeap: - AcquireSys(HeapLock) + acquireSys(HeapLock) template release(gch: TGcHeap) = when hasThreadSupport and hasSharedHeap: @@ -128,7 +136,7 @@ template setColor(c, col) = else: c.refcount = c.refCount and not colorMask or col -proc writeCell(msg: CString, c: PCell) = +proc writeCell(msg: cstring, c: PCell) = var kind = -1 if c.typ != nil: kind = ord(c.typ.kind) when leakDetector: @@ -159,6 +167,8 @@ else: template `++`(x: expr): stmt = Inc(x, rcIncrement) proc prepareDealloc(cell: PCell) = + when useMarkForDebug: + gcAssert(cell notin gch.marked, "Cell still alive!") if cell.typ.finalizer != nil: # the finalizer could invoke something that # allocates memory; this could trigger a garbage @@ -172,21 +182,21 @@ proc prepareDealloc(cell: PCell) = proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! when hasThreadSupport and hasSharedHeap: - AcquireSys(HeapLock) + acquireSys(HeapLock) when cycleGC: if c.color != rcPurple: c.setColor(rcPurple) incl(gch.cycleRoots, c) when hasThreadSupport and hasSharedHeap: - ReleaseSys(HeapLock) + releaseSys(HeapLock) proc rtlAddZCT(c: PCell) {.rtl, inl.} = # we MUST access gch as a global here, because this crosses DLL boundaries! when hasThreadSupport and hasSharedHeap: - AcquireSys(HeapLock) + acquireSys(HeapLock) addZCT(gch.zct, c) when hasThreadSupport and hasSharedHeap: - ReleaseSys(HeapLock) + releaseSys(HeapLock) proc decRef(c: PCell) {.inline.} = gcAssert(isAllocatedPtr(gch.region, c), "decRef: interiorPtr") @@ -249,7 +259,7 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} = # unsureAsgnRef updates the reference counters only if dest is not on the # stack. It is used by the code generator if it cannot decide wether a # reference is in the stack or not (this can happen for var parameters). - if not IsOnStack(dest): + if not isOnStack(dest): if src != nil: incRef(usrToCell(src)) # XXX finally use assembler for the stack checking instead! # the test for '!= nil' is correct, but I got tired of the segfaults @@ -277,6 +287,27 @@ proc initGC() = init(gch.tempStack) init(gch.cycleRoots) init(gch.decStack) + when useMarkForDebug or useBackupGc: + init(gch.marked) + +when useMarkForDebug or useBackupGc: + type + TGlobalMarkerProc = proc () {.nimcall.} + var + globalMarkersLen: int + globalMarkers: array[0.. 7_000, TGlobalMarkerProc] + + proc nimRegisterGlobalMarker(markerProc: TGlobalMarkerProc) {.compilerProc.} = + if globalMarkersLen <= high(globalMarkers): + globalMarkers[globalMarkersLen] = markerProc + inc globalMarkersLen + else: + echo "[GC] cannot register global variable; too many global variables" + quit 1 + +proc cellsetReset(s: var TCellSet) = + deinit(s) + init(s) proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) = var d = cast[TAddress](dest) @@ -534,7 +565,7 @@ proc collectWhite(s: PCell) = forAllChildren(s, waCollectWhite) freeCyclicCell(gch, s) -proc MarkRoots(gch: var TGcHeap) = +proc markRoots(gch: var TGcHeap) = var tabSize = 0 for s in elements(gch.cycleRoots): #writeCell("markRoot", s) @@ -548,6 +579,38 @@ proc MarkRoots(gch: var TGcHeap) = freeCyclicCell(gch, s) gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize) +when useBackupGc: + proc sweep(gch: var TGcHeap) = + for x in allObjects(gch.region): + if isCell(x): + # cast to PCell is correct here: + var c = cast[PCell](x) + if c notin gch.marked: freeCyclicCell(gch, c) + +when useMarkForDebug or useBackupGc: + proc markS(gch: var TGcHeap, c: PCell) = + incl(gch.marked, c) + gcAssert gch.tempStack.len == 0, "stack not empty!" + forAllChildren(c, waMarkPrecise) + while gch.tempStack.len > 0: + dec gch.tempStack.len + var d = gch.tempStack.d[gch.tempStack.len] + if not containsOrIncl(gch.marked, d): + forAllChildren(d, waMarkPrecise) + + proc markGlobals(gch: var TGcHeap) = + for i in 0 .. < globalMarkersLen: globalMarkers[i]() + + proc stackMarkS(gch: var TGcHeap, p: pointer) {.inline.} = + # the addresses are not as cells on the stack, so turn them to cells: + var cell = usrToCell(p) + var c = cast[TAddress](cell) + if c >% PageSize: + # fast check: does it look like a cell? + var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) + if objStart != nil: + markS(gch, objStart) + proc doOperation(p: pointer, op: TWalkOp) = if p == nil: return var c: PCell = usrToCell(p) @@ -580,12 +643,26 @@ proc doOperation(p: pointer, op: TWalkOp) = if c.color != rcBlack: scanBlack(c) of waCollectWhite: collectWhite(c) + of waMarkGlobal: + when useMarkForDebug or useBackupGc: + when hasThreadSupport: + # could point to a cell which we don't own and don't want to touch/trace + if isAllocatedPtr(gch.region, c): + markS(gch, c) + else: + markS(gch, c) + of waMarkPrecise: + when useMarkForDebug or useBackupGc: + add(gch.tempStack, c) proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, TWalkOp(op)) proc CollectZCT(gch: var TGcHeap): bool +when useMarkForDebug or useBackupGc: + proc markStackAndRegistersForSweep(gch: var TGcHeap) {.noinline, cdecl.} + proc collectRoots(gch: var TGcHeap) = for s in elements(gch.cycleRoots): excl(gch.cycleRoots, s) @@ -594,13 +671,18 @@ proc collectRoots(gch: var TGcHeap) = proc collectCycles(gch: var TGcHeap) = # ensure the ZCT 'color' is not used: while gch.zct.len > 0: discard collectZCT(gch) - markRoots(gch) - # scanRoots: - for s in elements(gch.cycleRoots): scan(s) - collectRoots(gch) + when useBackupGc: + cellsetReset(gch.marked) + markStackAndRegistersForSweep(gch) + markGlobals(gch) + sweep(gch) + else: + markRoots(gch) + # scanRoots: + for s in elements(gch.cycleRoots): scan(s) + collectRoots(gch) - Deinit(gch.cycleRoots) - Init(gch.cycleRoots) + cellsetReset(gch.cycleRoots) # alive cycles need to be kept in 'cycleRoots' if they are referenced # from the stack; otherwise the write barrier will add the cycle root again # anyway: @@ -695,7 +777,7 @@ when defined(sparc): # For SPARC architecture. var x = cast[TAddress](p) result = a <=% x and x <=% b - proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = + template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = when defined(sparcv9): asm """"flushw \n" """ else: @@ -731,7 +813,7 @@ elif stackIncreases: # a little hack to get the size of a TJmpBuf in the generated C code # in a platform independant way - proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = + template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = var registers: C_JmpBuf if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. var max = cast[TAddress](gch.stackBottom) @@ -754,7 +836,7 @@ else: var x = cast[TAddress](p) result = a <=% x and x <=% b - proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = + template forEachStackSlot(gch, gcMark: expr) {.immediate, dirty.} = # We use a jmp_buf buffer that is in the C stack. # Used to traverse the stack and registers assuming # that 'setjmp' will save registers in the C stack. @@ -778,12 +860,19 @@ else: while sp <=% max: gcMark(gch, cast[ppointer](sp)[]) sp = sp +% sizeof(pointer) + +proc markStackAndRegisters(gch: var TGcHeap) {.noinline, cdecl.} = + forEachStackSlot(gch, gcMark) + +when useMarkForDebug or useBackupGc: + proc markStackAndRegistersForSweep(gch: var TGcHeap) = + forEachStackSlot(gch, stackMarkS) # ---------------------------------------------------------------------------- # end of non-portable code # ---------------------------------------------------------------------------- -proc CollectZCT(gch: var TGcHeap): bool = +proc collectZCT(gch: var TGcHeap): bool = # Note: Freeing may add child objects to the ZCT! So essentially we do # deep freeing, which is bad for incremental operation. In order to # avoid a deep stack, we move objects to keep the ZCT small. @@ -880,10 +969,19 @@ proc collectCTBody(gch: var TGcHeap) = if gch.maxPause > 0 and duration > gch.maxPause: c_fprintf(c_stdout, "[GC] missed deadline: %ld\n", duration) +when useMarkForDebug or useBackupGc: + proc markForDebug(gch: var TGcHeap) = + markStackAndRegistersForSweep(gch) + markGlobals(gch) + proc collectCT(gch: var TGcHeap) = if (gch.zct.len >= ZctThreshold or (cycleGC and getOccupiedMem(gch.region)>=gch.cycleThreshold) or alwaysGC) and gch.recGcLock == 0: + when useMarkForDebug: + prepareForInteriorPointerChecking(gch.region) + cellsetReset(gch.marked) + markForDebug(gch) collectCTBody(gch) when withRealtime: diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 9f37e95c1..c9801abad 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -313,7 +313,7 @@ else: # XXX use 'compileOption' here include "system/gc_ms" elif defined(gcGenerational): - include "system/gc_genms" + include "system/gc" else: include "system/gc" diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 3c9ce73ac..a51864ac2 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -117,7 +117,8 @@ proc reprSet(p: pointer, typ: PNimType): string {.compilerRtl.} = type TReprClosure {.final.} = object # we cannot use a global variable here # as this wouldn't be thread-safe - marked: TCellSet + when defined(TCellSet): + marked: TCellSet recdepth: int # do not recurse endlessly indent: int # indentation @@ -127,12 +128,13 @@ when not defined(useNimRtl): # have to do it here ... when hasThreadSupport and hasSharedHeap and defined(heapLock): AcquireSys(HeapLock) - Init(cl.marked) + when defined(TCellSet): + Init(cl.marked) cl.recdepth = -1 # default is to display everything! cl.indent = 0 proc deinitReprClosure(cl: var TReprClosure) = - Deinit(cl.marked) + when defined(TCellSet): Deinit(cl.marked) when hasThreadSupport and hasSharedHeap and defined(heapLock): ReleaseSys(HeapLock) @@ -195,16 +197,17 @@ when not defined(useNimRtl): proc reprRef(result: var string, p: pointer, typ: PNimType, cl: var TReprClosure) = # we know that p is not nil here: - when defined(boehmGC) or defined(nogc): - var cell = cast[PCell](p) - else: - var cell = usrToCell(p) - add result, "ref " & reprPointer(p) - if cell notin cl.marked: - # only the address is shown: - incl(cl.marked, cell) - add result, " --> " - reprAux(result, p, typ.base, cl) + when defined(TCellSet): + when defined(boehmGC) or defined(nogc): + var cell = cast[PCell](p) + else: + var cell = usrToCell(p) + add result, "ref " & reprPointer(p) + if cell notin cl.marked: + # only the address is shown: + incl(cl.marked, cell) + add result, " --> " + reprAux(result, p, typ.base, cl) proc reprAux(result: var string, p: pointer, typ: PNimType, cl: var TReprClosure) = |