working cycle collector for old GC
This commit is contained in:
parent
5a31bc8274
commit
0bb3731422
1 changed files with 135 additions and 255 deletions
|
|
@ -38,14 +38,13 @@ const
|
||||||
rcGray = 0b001 # possible member of a cycle
|
rcGray = 0b001 # possible member of a cycle
|
||||||
rcWhite = 0b010 # member of a garbage cycle
|
rcWhite = 0b010 # member of a garbage cycle
|
||||||
rcPurple = 0b011 # possible root of a cycle
|
rcPurple = 0b011 # possible root of a cycle
|
||||||
rcZct = 0b100 # in ZCT
|
ZctFlag = 0b100 # in ZCT
|
||||||
rcRed = 0b101 # Candidate cycle undergoing sigma-computation
|
|
||||||
rcOrange = 0b110 # Candidate cycle awaiting epoch boundary
|
|
||||||
rcShift = 3 # shift by rcShift to get the reference counter
|
rcShift = 3 # shift by rcShift to get the reference counter
|
||||||
colorMask = 0b111
|
colorMask = 0b011
|
||||||
type
|
type
|
||||||
TWalkOp = enum
|
TWalkOp = enum
|
||||||
waZctDecRef, waPush, waCycleDecRef
|
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
|
||||||
|
waCollectWhite
|
||||||
|
|
||||||
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
|
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall.}
|
||||||
# A ref type can have a finalizer that is called before the object's
|
# A ref type can have a finalizer that is called before the object's
|
||||||
|
|
@ -95,8 +94,8 @@ template gcAssert(cond: bool, msg: string) =
|
||||||
quit 1
|
quit 1
|
||||||
|
|
||||||
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
proc addZCT(s: var TCellSeq, c: PCell) {.noinline.} =
|
||||||
if (c.refcount and rcZct) == 0:
|
if (c.refcount and ZctFlag) == 0:
|
||||||
c.refcount = c.refcount and not colorMask or rcZct
|
c.refcount = c.refcount or ZctFlag
|
||||||
add(s, c)
|
add(s, c)
|
||||||
|
|
||||||
proc cellToUsr(cell: PCell): pointer {.inline.} =
|
proc cellToUsr(cell: PCell): pointer {.inline.} =
|
||||||
|
|
@ -121,6 +120,13 @@ proc internRefcount(p: pointer): int {.exportc: "getRefcount".} =
|
||||||
when BitsPerPage mod (sizeof(int)*8) != 0:
|
when BitsPerPage mod (sizeof(int)*8) != 0:
|
||||||
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
{.error: "(BitsPerPage mod BitsPerUnit) should be zero!".}
|
||||||
|
|
||||||
|
template color(c): expr = c.refCount and colorMask
|
||||||
|
template setColor(c, col) =
|
||||||
|
when col == rcBlack:
|
||||||
|
c.refcount = c.refCount and not colorMask
|
||||||
|
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
|
var kind = -1
|
||||||
if c.typ != nil: kind = ord(c.typ.kind)
|
if c.typ != nil: kind = ord(c.typ.kind)
|
||||||
|
|
@ -128,60 +134,8 @@ proc writeCell(msg: CString, c: PCell) =
|
||||||
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n",
|
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld from %s(%ld)\n",
|
||||||
msg, c, kind, c.refcount shr rcShift, c.filename, c.line)
|
msg, c, kind, c.refcount shr rcShift, c.filename, c.line)
|
||||||
else:
|
else:
|
||||||
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld\n",
|
c_fprintf(c_stdout, "[GC] %s: %p %d rc=%ld; color=%ld\n",
|
||||||
msg, c, kind, c.refcount shr rcShift)
|
msg, c, kind, c.refcount shr rcShift, c.color)
|
||||||
|
|
||||||
when traceGC:
|
|
||||||
# traceGC is a special switch to enable extensive debugging
|
|
||||||
type
|
|
||||||
TCellState = enum
|
|
||||||
csAllocated, csZctFreed, csCycFreed
|
|
||||||
var
|
|
||||||
states: array[TCellState, TCellSet]
|
|
||||||
|
|
||||||
proc traceCell(c: PCell, state: TCellState) =
|
|
||||||
case state
|
|
||||||
of csAllocated:
|
|
||||||
if c in states[csAllocated]:
|
|
||||||
writeCell("attempt to alloc an already allocated cell", c)
|
|
||||||
sysAssert(false, "traceCell 1")
|
|
||||||
excl(states[csCycFreed], c)
|
|
||||||
excl(states[csZctFreed], c)
|
|
||||||
of csZctFreed:
|
|
||||||
if c in states[csZctFreed]:
|
|
||||||
writeCell("attempt to free zct cell twice", c)
|
|
||||||
sysAssert(false, "traceCell 2")
|
|
||||||
if c in states[csCycFreed]:
|
|
||||||
writeCell("attempt to free with zct, but already freed with cyc", c)
|
|
||||||
sysAssert(false, "traceCell 3")
|
|
||||||
if c notin states[csAllocated]:
|
|
||||||
writeCell("attempt to free not an allocated cell", c)
|
|
||||||
sysAssert(false, "traceCell 4")
|
|
||||||
excl(states[csAllocated], c)
|
|
||||||
of csCycFreed:
|
|
||||||
if c notin states[csAllocated]:
|
|
||||||
writeCell("attempt to free a not allocated cell", c)
|
|
||||||
sysAssert(false, "traceCell 5")
|
|
||||||
if c in states[csCycFreed]:
|
|
||||||
writeCell("attempt to free cyc cell twice", c)
|
|
||||||
sysAssert(false, "traceCell 6")
|
|
||||||
if c in states[csZctFreed]:
|
|
||||||
writeCell("attempt to free with cyc, but already freed with zct", c)
|
|
||||||
sysAssert(false, "traceCell 7")
|
|
||||||
excl(states[csAllocated], c)
|
|
||||||
incl(states[state], c)
|
|
||||||
|
|
||||||
proc writeLeakage() =
|
|
||||||
var z = 0
|
|
||||||
var y = 0
|
|
||||||
var e = 0
|
|
||||||
for c in elements(states[csAllocated]):
|
|
||||||
inc(e)
|
|
||||||
if c in states[csZctFreed]: 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)
|
|
||||||
|
|
||||||
template gcTrace(cell, state: expr): stmt {.immediate.} =
|
template gcTrace(cell, state: expr): stmt {.immediate.} =
|
||||||
when traceGC: traceCell(cell, state)
|
when traceGC: traceCell(cell, state)
|
||||||
|
|
@ -218,7 +172,9 @@ proc rtlAddCycleRoot(c: PCell) {.rtl, inl.} =
|
||||||
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
# we MUST access gch as a global here, because this crosses DLL boundaries!
|
||||||
when hasThreadSupport and hasSharedHeap:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
AcquireSys(HeapLock)
|
AcquireSys(HeapLock)
|
||||||
incl(gch.cycleRoots, c)
|
if c.color != rcPurple:
|
||||||
|
c.setColor(rcPurple)
|
||||||
|
incl(gch.cycleRoots, c)
|
||||||
when hasThreadSupport and hasSharedHeap:
|
when hasThreadSupport and hasSharedHeap:
|
||||||
ReleaseSys(HeapLock)
|
ReleaseSys(HeapLock)
|
||||||
|
|
||||||
|
|
@ -238,13 +194,15 @@ proc decRef(c: PCell) {.inline.} =
|
||||||
elif canBeCycleRoot(c):
|
elif canBeCycleRoot(c):
|
||||||
# unfortunately this is necessary here too, because a cycle might just
|
# unfortunately this is necessary here too, because a cycle might just
|
||||||
# have been broken up and we could recycle it.
|
# have been broken up and we could recycle it.
|
||||||
rtlAddCycleRoot(c)
|
rtlAddCycleRoot(c)
|
||||||
|
#writeCell("decRef", c)
|
||||||
|
|
||||||
proc incRef(c: PCell) {.inline.} =
|
proc incRef(c: PCell) {.inline.} =
|
||||||
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
|
gcAssert(isAllocatedPtr(gch.region, c), "incRef: interiorPtr")
|
||||||
++c.refcount
|
c.refcount = c.refCount +% rcIncrement and not colorMask
|
||||||
if canBeCycleRoot(c):
|
#writeCell("incRef", c)
|
||||||
rtlAddCycleRoot(c)
|
#if canBeCycleRoot(c):
|
||||||
|
# rtlAddCycleRoot(c)
|
||||||
|
|
||||||
proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p))
|
proc nimGCref(p: pointer) {.compilerProc, inline.} = incRef(usrToCell(p))
|
||||||
proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p))
|
proc nimGCunref(p: pointer) {.compilerProc, inline.} = decRef(usrToCell(p))
|
||||||
|
|
@ -290,14 +248,14 @@ proc unsureAsgnRef(dest: ppointer, src: pointer) {.compilerProc.} =
|
||||||
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
|
if cast[int](dest[]) >=% PageSize: decRef(usrToCell(dest[]))
|
||||||
else:
|
else:
|
||||||
# can't be an interior pointer if it's a stack location!
|
# can't be an interior pointer if it's a stack location!
|
||||||
gcAssert(interiorAllocatedPtr(gch.region, dest)==nil,
|
gcAssert(interiorAllocatedPtr(gch.region, dest) == nil,
|
||||||
"stack loc AND interior pointer")
|
"stack loc AND interior pointer")
|
||||||
dest[] = src
|
dest[] = src
|
||||||
|
|
||||||
proc initGC() =
|
proc initGC() =
|
||||||
when not defined(useNimRtl):
|
when not defined(useNimRtl):
|
||||||
when traceGC:
|
when traceGC:
|
||||||
for i in low(TCellState)..high(TCellState): Init(states[i])
|
for i in low(TCellState)..high(TCellState): init(states[i])
|
||||||
gch.cycleThreshold = InitialCycleThreshold
|
gch.cycleThreshold = InitialCycleThreshold
|
||||||
gch.stat.stackScans = 0
|
gch.stat.stackScans = 0
|
||||||
gch.stat.cycleCollections = 0
|
gch.stat.cycleCollections = 0
|
||||||
|
|
@ -308,8 +266,8 @@ proc initGC() =
|
||||||
# init the rt
|
# init the rt
|
||||||
init(gch.zct)
|
init(gch.zct)
|
||||||
init(gch.tempStack)
|
init(gch.tempStack)
|
||||||
Init(gch.cycleRoots)
|
init(gch.cycleRoots)
|
||||||
Init(gch.decStack)
|
init(gch.decStack)
|
||||||
|
|
||||||
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: TWalkOp) =
|
||||||
var d = cast[TAddress](dest)
|
var d = cast[TAddress](dest)
|
||||||
|
|
@ -383,7 +341,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
|
||||||
template replaceZctEntry(i: expr) =
|
template replaceZctEntry(i: expr) =
|
||||||
c = d[i]
|
c = d[i]
|
||||||
if c.refcount >=% rcIncrement:
|
if c.refcount >=% rcIncrement:
|
||||||
c.refcount = c.refcount and not colorMask
|
c.refcount = c.refcount and not ZctFlag
|
||||||
d[i] = res
|
d[i] = res
|
||||||
return
|
return
|
||||||
if L > 8:
|
if L > 8:
|
||||||
|
|
@ -404,7 +362,7 @@ proc addNewObjToZCT(res: PCell, gch: var TGcHeap) {.inline.} =
|
||||||
for i in countdown(L-1, max(0, L-8)):
|
for i in countdown(L-1, max(0, L-8)):
|
||||||
var c = d[i]
|
var c = d[i]
|
||||||
if c.refcount >=% rcIncrement:
|
if c.refcount >=% rcIncrement:
|
||||||
c.refcount = c.refcount and not colorMask
|
c.refcount = c.refcount and not ZctFlag
|
||||||
d[i] = res
|
d[i] = res
|
||||||
return
|
return
|
||||||
add(gch.zct, res)
|
add(gch.zct, res)
|
||||||
|
|
@ -423,7 +381,8 @@ proc rawNewObj(typ: PNimType, size: int, gch: var TGcHeap): pointer =
|
||||||
if framePtr != nil and framePtr.prev != nil:
|
if framePtr != nil and framePtr.prev != nil:
|
||||||
res.filename = framePtr.prev.filename
|
res.filename = framePtr.prev.filename
|
||||||
res.line = framePtr.prev.line
|
res.line = framePtr.prev.line
|
||||||
res.refcount = rcZct # refcount is zero, but mark it to be in the ZCT
|
# refcount is zero, color is black, but mark it to be in the ZCT
|
||||||
|
res.refcount = ZctFlag
|
||||||
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
sysAssert(isAllocatedPtr(gch.region, res), "newObj: 3")
|
||||||
# its refcount is zero, so add it to the ZCT:
|
# its refcount is zero, so add it to the ZCT:
|
||||||
addNewObjToZCT(res, gch)
|
addNewObjToZCT(res, gch)
|
||||||
|
|
@ -504,7 +463,7 @@ proc growObj(old: pointer, newsize: int, gch: var TGcHeap): pointer =
|
||||||
# add(gch.zct, res)
|
# add(gch.zct, res)
|
||||||
#else: # XXX: what to do here?
|
#else: # XXX: what to do here?
|
||||||
# decRef(ol)
|
# decRef(ol)
|
||||||
if (ol.refcount and colorMask) == rcZct:
|
if (ol.refcount and ZctFlag) != 0:
|
||||||
var j = gch.zct.len-1
|
var j = gch.zct.len-1
|
||||||
var d = gch.zct.d
|
var d = gch.zct.d
|
||||||
while j >= 0:
|
while j >= 0:
|
||||||
|
|
@ -534,30 +493,6 @@ proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
|
||||||
|
|
||||||
# ---------------- cycle collector -------------------------------------------
|
# ---------------- cycle collector -------------------------------------------
|
||||||
|
|
||||||
proc doOperation(p: pointer, op: TWalkOp) =
|
|
||||||
if p == nil: return
|
|
||||||
var c: PCell = usrToCell(p)
|
|
||||||
gcAssert(c != nil, "doOperation: 1")
|
|
||||||
case op # faster than function pointers because of easy prediction
|
|
||||||
of waZctDecRef:
|
|
||||||
#if not isAllocatedPtr(gch.region, c):
|
|
||||||
# return
|
|
||||||
# c_fprintf(c_stdout, "[GC] decref bug: %p", c)
|
|
||||||
gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
|
|
||||||
gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
|
|
||||||
c.refcount = c.refcount -% rcIncrement
|
|
||||||
when logGC: writeCell("decref (from doOperation)", c)
|
|
||||||
if c.refcount <% rcIncrement: addZCT(gch.zct, c)
|
|
||||||
# XXX bug here: needs the full write barrier
|
|
||||||
of waPush:
|
|
||||||
add(gch.tempStack, c)
|
|
||||||
of waCycleDecRef:
|
|
||||||
gcAssert(c.refcount >=% rcIncrement, "doOperation 3")
|
|
||||||
c.refcount = c.refcount -% rcIncrement
|
|
||||||
|
|
||||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
|
||||||
doOperation(d, TWalkOp(op))
|
|
||||||
|
|
||||||
proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
|
proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
|
||||||
prepareDealloc(c)
|
prepareDealloc(c)
|
||||||
gcTrace(c, csCycFreed)
|
gcTrace(c, csCycFreed)
|
||||||
|
|
@ -567,167 +502,113 @@ proc freeCyclicCell(gch: var TGcHeap, c: PCell) =
|
||||||
gcAssert(c.typ != nil, "freeCyclicCell")
|
gcAssert(c.typ != nil, "freeCyclicCell")
|
||||||
zeroMem(c, sizeof(TCell))
|
zeroMem(c, sizeof(TCell))
|
||||||
|
|
||||||
# we now use a much simpler and non-recursive algorithm for cycle removal
|
proc markGray(s: PCell) =
|
||||||
proc CollectZCT(gch: var TGcHeap): bool
|
if s.color != rcGray:
|
||||||
|
setColor(s, rcGray)
|
||||||
|
forAllChildren(s, waMarkGray)
|
||||||
|
|
||||||
when false:
|
proc scanBlack(s: PCell) =
|
||||||
template color(c): expr = c.refCount and colorMask
|
s.setColor(rcBlack)
|
||||||
template setColor(c, col) = c.refCount and not colorMask or col
|
forAllChildren(s, waScanBlack)
|
||||||
|
|
||||||
proc markGray(s: PCell) =
|
proc scan(s: PCell) =
|
||||||
if s.color != rcGray:
|
if s.color == rcGray:
|
||||||
setColor(s, rcGray)
|
if s.refcount >=% rcIncrement:
|
||||||
forAllChildren(s, waMarkGray)
|
|
||||||
|
|
||||||
proc scan(s: PCell) =
|
|
||||||
if s.color == rcGray:
|
|
||||||
scanBlack(s)
|
scanBlack(s)
|
||||||
else:
|
else:
|
||||||
s.setColor(rcWhite)
|
s.setColor(rcWhite)
|
||||||
forAllChildren(s, waScan)
|
forAllChildren(s, waScan)
|
||||||
|
|
||||||
|
proc collectWhite(s: PCell) =
|
||||||
|
if s.color == rcWhite and s notin gch.cycleRoots:
|
||||||
|
s.setcolor(rcBlack)
|
||||||
|
forAllChildren(s, waCollectWhite)
|
||||||
|
freeCyclicCell(gch, s)
|
||||||
|
|
||||||
proc scanBlack(s: PCell) =
|
proc MarkRoots(gch: var TGcHeap) =
|
||||||
s.setColor(rcBlack)
|
var tabSize = 0
|
||||||
forAllChildren(s, waScanBlack)
|
for s in elements(gch.cycleRoots):
|
||||||
|
#writeCell("markRoot", s)
|
||||||
proc collectWhite(s: PCell) =
|
inc tabSize
|
||||||
if s.color == rcWhite and not buffered(s):
|
if s.color == rcPurple and s.refCount >=% rcIncrement:
|
||||||
s.setcolor(rcBlack)
|
markGray(s)
|
||||||
forAllChildren(s, waCollectWhite)
|
else:
|
||||||
freeCyclicCell(gch, s)
|
excl(gch.cycleRoots, s)
|
||||||
|
# (s.color == rcBlack and rc == 0) as 1 condition:
|
||||||
|
if s.refcount == 0:
|
||||||
|
freeCyclicCell(gch, s)
|
||||||
|
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
|
||||||
|
|
||||||
proc MarkRoots(gch: var TGcHeap) =
|
proc doOperation(p: pointer, op: TWalkOp) =
|
||||||
for s in elements(gch.cycleRoots):
|
if p == nil: return
|
||||||
if s.color == rcPurple and s.refCount >=% rcIncrement:
|
var c: PCell = usrToCell(p)
|
||||||
markGray(s)
|
gcAssert(c != nil, "doOperation: 1")
|
||||||
else:
|
# the 'case' should be faster than function pointers because of easy
|
||||||
# since we cannot remove from 'cycleRoots' easily, we use the ZCT as
|
# prediction:
|
||||||
# a temporary buffer:
|
case op
|
||||||
addZCT(gch.zct, s)
|
of waZctDecRef:
|
||||||
var freed = 0
|
#if not isAllocatedPtr(gch.region, c):
|
||||||
for i in 0 .. < gch.zct.len:
|
# return
|
||||||
let c = gch.zct.d[i]
|
# c_fprintf(c_stdout, "[GC] decref bug: %p", c)
|
||||||
# if black and rc == 0:
|
gcAssert(isAllocatedPtr(gch.region, c), "decRef: waZctDecRef")
|
||||||
excl(gch.cycleRoots, c)
|
gcAssert(c.refcount >=% rcIncrement, "doOperation 2")
|
||||||
if c.refcount == 0:
|
#c.refcount = c.refcount -% rcIncrement
|
||||||
freeCyclicCell(gch, c)
|
when logGC: writeCell("decref (from doOperation)", c)
|
||||||
inc freed
|
decRef(c)
|
||||||
|
#if c.refcount <% rcIncrement: addZCT(gch.zct, c)
|
||||||
|
of waPush:
|
||||||
|
add(gch.tempStack, c)
|
||||||
|
of waCycleDecRef:
|
||||||
|
gcAssert(c.refcount >=% rcIncrement, "doOperation 3")
|
||||||
|
c.refcount = c.refcount -% rcIncrement
|
||||||
|
of waMarkGray:
|
||||||
|
gcAssert(c.refcount >=% rcIncrement, "waMarkGray")
|
||||||
|
c.refcount = c.refcount -% rcIncrement
|
||||||
|
markGray(c)
|
||||||
|
of waScan: scan(c)
|
||||||
|
of waScanBlack:
|
||||||
|
c.refcount = c.refcount +% rcIncrement
|
||||||
|
if c.color != rcBlack:
|
||||||
|
scanBlack(c)
|
||||||
|
of waCollectWhite: collectWhite(c)
|
||||||
|
|
||||||
proc collectRoots(gch: var TGcHeap) =
|
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||||
for s in elements(gch.cycleRoots):
|
doOperation(d, TWalkOp(op))
|
||||||
|
|
||||||
collectWhite(s)
|
|
||||||
|
|
||||||
proc collectCycles(gch: var TGcHeap) =
|
proc CollectZCT(gch: var TGcHeap): bool
|
||||||
while gch.zct.len > 0: discard collectZCT(gch)
|
|
||||||
markRoots(gch)
|
|
||||||
scanRoots(gch)
|
|
||||||
collectRoots(gch)
|
|
||||||
|
|
||||||
var tabSize = 0
|
|
||||||
# while RemoveInnerRCs, we misuse the ZCT as a "candidates to be freed"
|
|
||||||
# buffer; the ZCT is guaranteed to be empty here.
|
|
||||||
# However, since the RC is in flux in the following traversals, it can be
|
|
||||||
# that we store cells with RC > 0 in the ZCT. This needs to be checked for
|
|
||||||
# in the final loop over the ZCT.
|
|
||||||
var marker: TCellSet
|
|
||||||
Init(marker)
|
|
||||||
var
|
|
||||||
decs = 0
|
|
||||||
incs = 0
|
|
||||||
for c in elements(gch.cycleRoots):
|
|
||||||
inc(tabSize)
|
|
||||||
if c.refcount >=% rcIncrement and not containsOrIncl(marker, c):
|
|
||||||
gch.tempStack.len = 0
|
|
||||||
forAllChildren(c, waPush)
|
|
||||||
while gch.tempStack.len > 0:
|
|
||||||
dec(gch.tempStack.len)
|
|
||||||
var d = gch.tempStack.d[gch.tempStack.len]
|
|
||||||
gcAssert d.refcount >=% rcIncrement, "child's RC corrupted!"
|
|
||||||
d.refcount = d.refcount -% rcIncrement
|
|
||||||
writeCell("decref (cycle)", d)
|
|
||||||
inc decs
|
|
||||||
if d.refcount <% rcIncrement:
|
|
||||||
addZCT(gch.zct, d)
|
|
||||||
if not containsOrIncl(marker, d):
|
|
||||||
forAllChildren(d, waPush)
|
|
||||||
#forallChildren(c, waCycleDecRef)
|
|
||||||
if tabSize == 0: return
|
|
||||||
gch.stat.cycleTableSize = max(gch.stat.cycleTableSize, tabSize)
|
|
||||||
|
|
||||||
# restore reference counts (a depth-first traversal is needed);
|
proc collectRoots(gch: var TGcHeap) =
|
||||||
# We need to restore the cycle roots with RC > 0 plus the marked
|
for s in elements(gch.cycleRoots):
|
||||||
for c in elements(gch.cycleRoots):
|
excl(gch.cycleRoots, s)
|
||||||
excl(marker, c)
|
collectWhite(s)
|
||||||
if c.refcount >=% rcIncrement:
|
|
||||||
gch.tempStack.len = 0
|
|
||||||
var loopIter = 0
|
|
||||||
forAllChildren(c, waPush)
|
|
||||||
while gch.tempStack.len > 0:
|
|
||||||
dec(gch.tempStack.len)
|
|
||||||
var d = gch.tempStack.d[gch.tempStack.len]
|
|
||||||
d.refcount = d.refcount +% rcIncrement
|
|
||||||
writeCell("incref (cycle)", d)
|
|
||||||
writeCell("from ", c)
|
|
||||||
cfprintf(cstdout, "depth: %ld\n", loopIter)
|
|
||||||
inc incs
|
|
||||||
if contains(marker, d):
|
|
||||||
excl(marker, d)
|
|
||||||
inc loopIter
|
|
||||||
forAllChildren(d, waPush)
|
|
||||||
gcAssert incs <= decs, "too many increments!"
|
|
||||||
Deinit(marker)
|
|
||||||
# remove cycles: free nodes with RC == 0, but do nothing with their children:
|
|
||||||
var freed = 0
|
|
||||||
for i in 0 .. < gch.zct.len:
|
|
||||||
let c = gch.zct.d[i]
|
|
||||||
if c.refcount <% rcIncrement:
|
|
||||||
freeCyclicCell(gch, c)
|
|
||||||
inc freed
|
|
||||||
cfprintf(cstdout, "freed cyclic objects: %ld; zct: %ld; decs: %ld; incs: %ld\n",
|
|
||||||
freed, gch.zct.len, decs, incs)
|
|
||||||
gch.zct.len = 0
|
|
||||||
if freed == 0:
|
|
||||||
gcAssert incs == decs, "graph corrupted!"
|
|
||||||
|
|
||||||
when false:
|
|
||||||
gcAssert gch.tempStack.len == 0, "tempStack not empty (A)"
|
|
||||||
gch.tempStack.len = 0
|
|
||||||
for c in elements(gch.cycleRoots):
|
|
||||||
if c.refcount <% rcIncrement:
|
|
||||||
gcAssert gch.tempStack.len == 0, "tempStack not empty (B)"
|
|
||||||
forAllChildren(c, waPush)
|
|
||||||
while gch.tempStack.len > 0:
|
|
||||||
dec(gch.tempStack.len)
|
|
||||||
var d = gch.tempStack.d[gch.tempStack.len]
|
|
||||||
if d.refcount <% rcIncrement:
|
|
||||||
if d notin gch.cycleRoots: # d is leaf of c and not part of cycle
|
|
||||||
freeCyclicCell(gch, d)
|
|
||||||
when logGC: writeCell("add to ZCT (from cycle collector)", d)
|
|
||||||
freeCyclicCell(gch, c)
|
|
||||||
Deinit(gch.cycleRoots)
|
|
||||||
Init(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!
|
|
||||||
when false:
|
|
||||||
block addBackStackRoots:
|
|
||||||
var d = gch.decStack.d
|
|
||||||
var cycleRootsLen = 0
|
|
||||||
for i in 0..gch.decStack.len-1:
|
|
||||||
var c = d[i]
|
|
||||||
gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots"
|
|
||||||
gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell"
|
|
||||||
if canBeCycleRoot(c):
|
|
||||||
if c notin gch.cycleRoots: inc cycleRootsLen
|
|
||||||
incl(gch.cycleRoots, c)
|
|
||||||
gcAssert c.typ != nil, "addBackStackRoots 2"
|
|
||||||
if cycleRootsLen != 0:
|
|
||||||
cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen)
|
|
||||||
|
|
||||||
proc collectCycles(gch: var TGcHeap) =
|
proc collectCycles(gch: var TGcHeap) =
|
||||||
# it's broken anyway
|
# ensure the ZCT 'color' is not used:
|
||||||
nil
|
while gch.zct.len > 0: discard collectZCT(gch)
|
||||||
|
markRoots(gch)
|
||||||
|
# scanRoots:
|
||||||
|
for s in elements(gch.cycleRoots): scan(s)
|
||||||
|
collectRoots(gch)
|
||||||
|
|
||||||
|
Deinit(gch.cycleRoots)
|
||||||
|
Init(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:
|
||||||
|
when false:
|
||||||
|
var d = gch.decStack.d
|
||||||
|
var cycleRootsLen = 0
|
||||||
|
for i in 0..gch.decStack.len-1:
|
||||||
|
var c = d[i]
|
||||||
|
gcAssert isAllocatedPtr(gch.region, c), "addBackStackRoots"
|
||||||
|
gcAssert c.refcount >=% rcIncrement, "addBackStackRoots: dead cell"
|
||||||
|
if canBeCycleRoot(c):
|
||||||
|
#if c notin gch.cycleRoots:
|
||||||
|
inc cycleRootsLen
|
||||||
|
incl(gch.cycleRoots, c)
|
||||||
|
gcAssert c.typ != nil, "addBackStackRoots 2"
|
||||||
|
if cycleRootsLen != 0:
|
||||||
|
cfprintf(cstdout, "cycle roots: %ld\n", cycleRootsLen)
|
||||||
|
|
||||||
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
|
proc gcMark(gch: var TGcHeap, p: pointer) {.inline.} =
|
||||||
# the addresses are not as cells on the stack, so turn them to cells:
|
# the addresses are not as cells on the stack, so turn them to cells:
|
||||||
|
|
@ -909,9 +790,9 @@ proc CollectZCT(gch: var TGcHeap): bool =
|
||||||
var c = gch.zct.d[0]
|
var c = gch.zct.d[0]
|
||||||
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
|
sysAssert(isAllocatedPtr(gch.region, c), "CollectZCT: isAllocatedPtr")
|
||||||
# remove from ZCT:
|
# remove from ZCT:
|
||||||
sysAssert((c.refcount and rcZct) == rcZct, "collectZCT")
|
gcAssert((c.refcount and ZctFlag) == ZctFlag, "collectZCT")
|
||||||
|
|
||||||
c.refcount = c.refcount and not colorMask
|
c.refcount = c.refcount and not ZctFlag
|
||||||
gch.zct.d[0] = gch.zct.d[L[] - 1]
|
gch.zct.d[0] = gch.zct.d[L[] - 1]
|
||||||
dec(L[])
|
dec(L[])
|
||||||
when withRealtime: dec steps
|
when withRealtime: dec steps
|
||||||
|
|
@ -946,16 +827,16 @@ proc CollectZCT(gch: var TGcHeap): bool =
|
||||||
return false
|
return false
|
||||||
result = true
|
result = true
|
||||||
|
|
||||||
proc unmarkStackAndRegisters(gch: var TGcHeap) =
|
proc unmarkStackAndRegisters(gch: var TGcHeap) =
|
||||||
var d = gch.decStack.d
|
var d = gch.decStack.d
|
||||||
for i in 0..gch.decStack.len-1:
|
for i in 0..gch.decStack.len-1:
|
||||||
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
|
sysAssert isAllocatedPtr(gch.region, d[i]), "unmarkStackAndRegisters"
|
||||||
# decRef(d[i]) inlined: cannot create a cycle and must not acquire lock
|
decRef(d[i])
|
||||||
var c = d[i]
|
#var c = d[i]
|
||||||
# XXX no need for an atomic dec here:
|
# XXX no need for an atomic dec here:
|
||||||
if --c.refcount:
|
#if --c.refcount:
|
||||||
addZCT(gch.zct, c)
|
# addZCT(gch.zct, c)
|
||||||
sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
|
#sysAssert c.typ != nil, "unmarkStackAndRegisters 2"
|
||||||
gch.decStack.len = 0
|
gch.decStack.len = 0
|
||||||
|
|
||||||
proc collectCTBody(gch: var TGcHeap) =
|
proc collectCTBody(gch: var TGcHeap) =
|
||||||
|
|
@ -1060,7 +941,6 @@ when not defined(useNimRtl):
|
||||||
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
"[GC] max cycle table size: " & $gch.stat.cycleTableSize & "\n" &
|
||||||
"[GC] max stack size: " & $gch.stat.maxStackSize & "\n" &
|
"[GC] max stack size: " & $gch.stat.maxStackSize & "\n" &
|
||||||
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
|
"[GC] max pause time [ms]: " & $(gch.stat.maxPause div 1000_000)
|
||||||
when traceGC: writeLeakage()
|
|
||||||
GC_enable()
|
GC_enable()
|
||||||
|
|
||||||
{.pop.}
|
{.pop.}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue