GC: even more stress testing
This commit is contained in:
parent
eb72857ea2
commit
f73cec7f01
4 changed files with 30 additions and 9 deletions
|
|
@ -13,7 +13,7 @@ Introduction
|
||||||
This document describes how the GC works and how to tune it for
|
This document describes how the GC works and how to tune it for
|
||||||
(soft) `realtime systems`:idx:.
|
(soft) `realtime systems`:idx:.
|
||||||
|
|
||||||
The basic algorithm is *Deferrent Reference Counting* with cycle detection.
|
The basic algorithm is *Deferred Reference Counting* with cycle detection.
|
||||||
References on the stack are not counted for better performance (and easier C
|
References on the stack are not counted for better performance (and easier C
|
||||||
code generation). The GC **never** scans the whole heap but it may scan the
|
code generation). The GC **never** scans the whole heap but it may scan the
|
||||||
delta-subgraph of the heap that changed since its last run.
|
delta-subgraph of the heap that changed since its last run.
|
||||||
|
|
|
||||||
|
|
@ -520,11 +520,18 @@ proc allocInv(a: TMemRegion): bool =
|
||||||
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
|
for s in low(a.freeSmallChunks)..high(a.freeSmallChunks):
|
||||||
var c = a.freeSmallChunks[s]
|
var c = a.freeSmallChunks[s]
|
||||||
while c != nil:
|
while c != nil:
|
||||||
if c.next == c: return false
|
if c.next == c:
|
||||||
if c.size != s * MemAlign: return false
|
echo "[SYSASSERT] c.next == c"
|
||||||
|
return false
|
||||||
|
if c.size != s * MemAlign:
|
||||||
|
echo "[SYSASSERT] c.size != s * MemAlign"
|
||||||
|
return false
|
||||||
var it = c.freeList
|
var it = c.freeList
|
||||||
while it != nil:
|
while it != nil:
|
||||||
if it.zeroField != 0: return false
|
if it.zeroField != 0:
|
||||||
|
echo "[SYSASSERT] it.zeroField != 0"
|
||||||
|
cprintf("%ld %p\n", it.zeroField, it)
|
||||||
|
return false
|
||||||
it = it.next
|
it = it.next
|
||||||
c = c.next
|
c = c.next
|
||||||
result = true
|
result = true
|
||||||
|
|
@ -591,6 +598,7 @@ proc rawAlloc(a: var TMemRegion, requestedSize: int): pointer =
|
||||||
add(a, a.root, cast[TAddress](result), cast[TAddress](result)+%size)
|
add(a, a.root, cast[TAddress](result), cast[TAddress](result)+%size)
|
||||||
sysAssert(isAccessible(a, result), "rawAlloc 14")
|
sysAssert(isAccessible(a, result), "rawAlloc 14")
|
||||||
sysAssert(allocInv(a), "rawAlloc: end")
|
sysAssert(allocInv(a), "rawAlloc: end")
|
||||||
|
when logAlloc: cprintf("rawAlloc: %ld %p\n", requestedSize, result)
|
||||||
|
|
||||||
proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
|
proc rawAlloc0(a: var TMemRegion, requestedSize: int): pointer =
|
||||||
result = rawAlloc(a, requestedSize)
|
result = rawAlloc(a, requestedSize)
|
||||||
|
|
@ -638,6 +646,7 @@ proc rawDealloc(a: var TMemRegion, p: pointer) =
|
||||||
del(a, a.root, cast[int](addr(c.data)))
|
del(a, a.root, cast[int](addr(c.data)))
|
||||||
freeBigChunk(a, c)
|
freeBigChunk(a, c)
|
||||||
sysAssert(allocInv(a), "rawDealloc: end")
|
sysAssert(allocInv(a), "rawDealloc: end")
|
||||||
|
when logAlloc: cprintf("rawDealloc: %p\n", p)
|
||||||
|
|
||||||
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
|
proc isAllocatedPtr(a: TMemRegion, p: pointer): bool =
|
||||||
if isAccessible(a, p):
|
if isAccessible(a, p):
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
# Garbage Collector
|
# Garbage Collector
|
||||||
#
|
#
|
||||||
# The basic algorithm is *Deferrent Reference Counting* with cycle detection.
|
# The basic algorithm is *Deferred Reference Counting* with cycle detection.
|
||||||
# This is achieved by combining a Deutsch-Bobrow garbage collector
|
# This is achieved by combining a Deutsch-Bobrow garbage collector
|
||||||
# together with Christoper's partial mark-sweep garbage collector.
|
# together with Christoper's partial mark-sweep garbage collector.
|
||||||
#
|
#
|
||||||
|
|
@ -407,12 +407,17 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
|
||||||
return
|
return
|
||||||
add(gch.zct, res)
|
add(gch.zct, res)
|
||||||
|
|
||||||
|
{.push stackTrace: off, profiler:off.}
|
||||||
|
proc gcInvariant*(msg: string) =
|
||||||
|
sysAssert(allocInv(gch.region), msg)
|
||||||
|
{.pop.}
|
||||||
|
|
||||||
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
|
proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
|
||||||
# generates a new object and sets its reference counter to 0
|
# generates a new object and sets its reference counter to 0
|
||||||
|
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
||||||
acquire(gch)
|
acquire(gch)
|
||||||
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1")
|
||||||
collectCT(gch)
|
collectCT(gch)
|
||||||
sysAssert(allocInv(gch.region), "rawNewObj begin")
|
|
||||||
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell)))
|
var res = cast[PCell](rawAlloc(gch.region, size + sizeof(TCell)))
|
||||||
gcAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
gcAssert((cast[TAddress](res) and (MemAlign-1)) == 0, "newObj: 2")
|
||||||
# now it is buffered in the ZCT
|
# now it is buffered in the ZCT
|
||||||
|
|
@ -517,7 +522,9 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
|
||||||
writeCell("growObj new cell", res)
|
writeCell("growObj new cell", res)
|
||||||
gcTrace(ol, csZctFreed)
|
gcTrace(ol, csZctFreed)
|
||||||
gcTrace(res, csAllocated)
|
gcTrace(res, csAllocated)
|
||||||
when reallyDealloc: rawDealloc(gch.region, ol)
|
when reallyDealloc:
|
||||||
|
sysAssert(allocInv(gch.region), "growObj before dealloc")
|
||||||
|
rawDealloc(gch.region, ol)
|
||||||
else:
|
else:
|
||||||
sysAssert(ol.typ != nil, "growObj: 5")
|
sysAssert(ol.typ != nil, "growObj: 5")
|
||||||
zeroMem(ol, sizeof(TCell))
|
zeroMem(ol, sizeof(TCell))
|
||||||
|
|
@ -537,7 +544,9 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
gcTrace(c, csCycFreed)
|
gcTrace(c, csCycFreed)
|
||||||
when logGC: writeCell("cycle collector dealloc cell", c)
|
when logGC: writeCell("cycle collector dealloc cell", c)
|
||||||
when reallyDealloc: rawDealloc(gch.region, c)
|
when reallyDealloc:
|
||||||
|
sysAssert(allocInv(gch.region), "free cyclic cell")
|
||||||
|
rawDealloc(gch.region, c)
|
||||||
else:
|
else:
|
||||||
gcAssert(c.typ != nil, "freeCyclicCell")
|
gcAssert(c.typ != nil, "freeCyclicCell")
|
||||||
zeroMem(c, sizeof(TCell))
|
zeroMem(c, sizeof(TCell))
|
||||||
|
|
@ -910,7 +919,9 @@ proc collectZCT(gch: var TGcHeap): bool =
|
||||||
# access invalid memory. This is done by prepareDealloc():
|
# access invalid memory. This is done by prepareDealloc():
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
forAllChildren(c, waZctDecRef)
|
forAllChildren(c, waZctDecRef)
|
||||||
when reallyDealloc: rawDealloc(gch.region, c)
|
when reallyDealloc:
|
||||||
|
sysAssert(allocInv(gch.region), "collectZCT: rawDealloc")
|
||||||
|
rawDealloc(gch.region, c)
|
||||||
else:
|
else:
|
||||||
sysAssert(c.typ != nil, "collectZCT 2")
|
sysAssert(c.typ != nil, "collectZCT 2")
|
||||||
zeroMem(c, sizeof(TCell))
|
zeroMem(c, sizeof(TCell))
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ const
|
||||||
reallyOsDealloc = true
|
reallyOsDealloc = true
|
||||||
coalescRight = true
|
coalescRight = true
|
||||||
coalescLeft = true
|
coalescLeft = true
|
||||||
|
logAlloc = false
|
||||||
|
|
||||||
type
|
type
|
||||||
PPointer = ptr pointer
|
PPointer = ptr pointer
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue