fixes #1796
This commit is contained in:
parent
e751a0af57
commit
7524610b31
5 changed files with 66 additions and 11 deletions
|
|
@ -49,7 +49,7 @@ type
|
||||||
waMarkGlobal, # part of the backup/debug mark&sweep
|
waMarkGlobal, # part of the backup/debug mark&sweep
|
||||||
waMarkPrecise, # part of the backup/debug mark&sweep
|
waMarkPrecise, # part of the backup/debug mark&sweep
|
||||||
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
|
waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack,
|
||||||
waCollectWhite,
|
waCollectWhite #, waDebug
|
||||||
|
|
||||||
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
|
TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
|
||||||
# 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
|
||||||
|
|
@ -595,9 +595,15 @@ proc scan(s: PCell) =
|
||||||
else:
|
else:
|
||||||
s.setColor(rcWhite)
|
s.setColor(rcWhite)
|
||||||
forAllChildren(s, waScan)
|
forAllChildren(s, waScan)
|
||||||
|
|
||||||
proc collectWhite(s: PCell) =
|
proc collectWhite(s: PCell) =
|
||||||
if s.color == rcWhite and s notin gch.cycleRoots:
|
# This is a hacky way to deal with the following problem (bug #1796)
|
||||||
|
# Consider this content in cycleRoots:
|
||||||
|
# x -> a; y -> a where 'a' is an acyclic object so not included in
|
||||||
|
# cycleRoots itself. Then 'collectWhite' used to free 'a' twice. The
|
||||||
|
# 'isAllocatedPtr' check prevents this. This also means we do not need
|
||||||
|
# to query 's notin gch.cycleRoots' at all.
|
||||||
|
if isAllocatedPtr(gch.region, s) and s.color == rcWhite:
|
||||||
s.setColor(rcBlack)
|
s.setColor(rcBlack)
|
||||||
forAllChildren(s, waCollectWhite)
|
forAllChildren(s, waCollectWhite)
|
||||||
freeCyclicCell(gch, s)
|
freeCyclicCell(gch, s)
|
||||||
|
|
@ -648,6 +654,28 @@ when useMarkForDebug or useBackupGc:
|
||||||
if objStart != nil:
|
if objStart != nil:
|
||||||
markS(gch, objStart)
|
markS(gch, objStart)
|
||||||
|
|
||||||
|
when logGC:
|
||||||
|
var
|
||||||
|
cycleCheckA: array[100, PCell]
|
||||||
|
cycleCheckALen = 0
|
||||||
|
|
||||||
|
proc alreadySeen(c: PCell): bool =
|
||||||
|
for i in 0 .. <cycleCheckALen:
|
||||||
|
if cycleCheckA[i] == c: return true
|
||||||
|
if cycleCheckALen == len(cycleCheckA):
|
||||||
|
gcAssert(false, "cycle detection overflow")
|
||||||
|
quit 1
|
||||||
|
cycleCheckA[cycleCheckALen] = c
|
||||||
|
inc cycleCheckALen
|
||||||
|
|
||||||
|
proc debugGraph(s: PCell) =
|
||||||
|
if alreadySeen(s):
|
||||||
|
writeCell("child cell (already seen) ", s)
|
||||||
|
else:
|
||||||
|
writeCell("cell {", s)
|
||||||
|
forAllChildren(s, waDebug)
|
||||||
|
c_fprintf(c_stdout, "}\n")
|
||||||
|
|
||||||
proc doOperation(p: pointer, op: TWalkOp) =
|
proc doOperation(p: pointer, op: TWalkOp) =
|
||||||
if p == nil: return
|
if p == nil: return
|
||||||
var c: PCell = usrToCell(p)
|
var c: PCell = usrToCell(p)
|
||||||
|
|
@ -690,6 +718,7 @@ proc doOperation(p: pointer, op: TWalkOp) =
|
||||||
of waMarkPrecise:
|
of waMarkPrecise:
|
||||||
when useMarkForDebug or useBackupGc:
|
when useMarkForDebug or useBackupGc:
|
||||||
add(gch.tempStack, c)
|
add(gch.tempStack, c)
|
||||||
|
#of waDebug: debugGraph(c)
|
||||||
|
|
||||||
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
|
||||||
doOperation(d, TWalkOp(op))
|
doOperation(d, TWalkOp(op))
|
||||||
|
|
@ -702,7 +731,6 @@ when useMarkForDebug or useBackupGc:
|
||||||
|
|
||||||
proc collectRoots(gch: var TGcHeap) =
|
proc collectRoots(gch: var TGcHeap) =
|
||||||
for s in elements(gch.cycleRoots):
|
for s in elements(gch.cycleRoots):
|
||||||
excl(gch.cycleRoots, s)
|
|
||||||
collectWhite(s)
|
collectWhite(s)
|
||||||
|
|
||||||
proc collectCycles(gch: var TGcHeap) =
|
proc collectCycles(gch: var TGcHeap) =
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ from strutils import join
|
||||||
type
|
type
|
||||||
TFoo * = object
|
TFoo * = object
|
||||||
id: int
|
id: int
|
||||||
func: proc(){.closure.}
|
fn: proc(){.closure.}
|
||||||
var foo_counter = 0
|
var foo_counter = 0
|
||||||
var alive_foos = newseq[int](0)
|
var alive_foos = newseq[int](0)
|
||||||
|
|
||||||
|
|
@ -26,7 +26,7 @@ for i in 0 .. <10:
|
||||||
|
|
||||||
for i in 0 .. <10:
|
for i in 0 .. <10:
|
||||||
let f = newFoo()
|
let f = newFoo()
|
||||||
f.func = proc =
|
f.fn = proc =
|
||||||
echo f.id
|
echo f.id
|
||||||
|
|
||||||
GC_fullcollect()
|
GC_fullcollect()
|
||||||
|
|
|
||||||
21
tests/gc/cyclecollector.nim
Normal file
21
tests/gc/cyclecollector.nim
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
|
||||||
|
# Program to detect bug #1796 reliably
|
||||||
|
|
||||||
|
type
|
||||||
|
Node = ref object
|
||||||
|
a, b: Node
|
||||||
|
leaf: string
|
||||||
|
|
||||||
|
proc createCycle(leaf: string): Node =
|
||||||
|
new result
|
||||||
|
result.a = result
|
||||||
|
shallowCopy result.leaf, leaf
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
for i in 0 .. 100_000:
|
||||||
|
var leaf = "this is the leaf. it allocates"
|
||||||
|
let x = createCycle(leaf)
|
||||||
|
let y = createCycle(leaf)
|
||||||
|
echo "done ", getOccupiedMem()
|
||||||
|
|
||||||
|
main()
|
||||||
|
|
@ -196,7 +196,8 @@ write(stdout, "starting main...\n")
|
||||||
main()
|
main()
|
||||||
|
|
||||||
GC_fullCollect()
|
GC_fullCollect()
|
||||||
|
# the M&S GC fails with this call and it's unclear why. Definitely something
|
||||||
|
# we need to fix!
|
||||||
GC_fullCollect()
|
GC_fullCollect()
|
||||||
writeln(stdout, GC_getStatistics())
|
writeln(stdout, GC_getStatistics())
|
||||||
write(stdout, "finished\n")
|
write(stdout, "finished\n")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -107,12 +107,15 @@ proc dllTests(r: var TResults, cat: Category, options: string) =
|
||||||
# ------------------------------ GC tests -------------------------------------
|
# ------------------------------ GC tests -------------------------------------
|
||||||
|
|
||||||
proc gcTests(r: var TResults, cat: Category, options: string) =
|
proc gcTests(r: var TResults, cat: Category, options: string) =
|
||||||
template test(filename: expr): stmt =
|
template testWithoutMs(filename: expr): stmt =
|
||||||
testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun)
|
testSpec r, makeTest("tests/gc" / filename, options, cat, actionRun)
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
" -d:release", cat, actionRun)
|
" -d:release", cat, actionRun)
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
" -d:release -d:useRealtimeGC", cat, actionRun)
|
" -d:release -d:useRealtimeGC", cat, actionRun)
|
||||||
|
|
||||||
|
template test(filename: expr): stmt =
|
||||||
|
testWithoutMs filename
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
" --gc:markAndSweep", cat, actionRun)
|
" --gc:markAndSweep", cat, actionRun)
|
||||||
testSpec r, makeTest("tests/gc" / filename, options &
|
testSpec r, makeTest("tests/gc" / filename, options &
|
||||||
|
|
@ -124,13 +127,15 @@ proc gcTests(r: var TResults, cat: Category, options: string) =
|
||||||
test "gctest"
|
test "gctest"
|
||||||
test "gcleak3"
|
test "gcleak3"
|
||||||
test "gcleak4"
|
test "gcleak4"
|
||||||
test "gcleak5"
|
# Disabled because it works and takes too long to run:
|
||||||
|
#test "gcleak5"
|
||||||
test "weakrefs"
|
test "weakrefs"
|
||||||
test "cycleleak"
|
test "cycleleak"
|
||||||
test "closureleak"
|
test "closureleak"
|
||||||
test "refarrayleak"
|
testWithoutMs "refarrayleak"
|
||||||
test "stackrefleak"
|
|
||||||
|
|
||||||
|
test "stackrefleak"
|
||||||
|
test "cyclecollector"
|
||||||
|
|
||||||
# ------------------------- threading tests -----------------------------------
|
# ------------------------- threading tests -----------------------------------
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue