diff options
-rw-r--r-- | lib/system/gc.nim | 36 | ||||
-rw-r--r-- | tests/gc/closureleak.nim | 4 | ||||
-rw-r--r-- | tests/gc/cyclecollector.nim | 21 | ||||
-rw-r--r-- | tests/gc/gctest.nim | 3 | ||||
-rw-r--r-- | tests/testament/categories.nim | 13 |
5 files changed, 66 insertions, 11 deletions
diff --git a/lib/system/gc.nim b/lib/system/gc.nim index e0db3fba4..58587cf7f 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -49,7 +49,7 @@ type waMarkGlobal, # part of the backup/debug mark&sweep waMarkPrecise, # part of the backup/debug mark&sweep waZctDecRef, waPush, waCycleDecRef, waMarkGray, waScan, waScanBlack, - waCollectWhite, + waCollectWhite #, waDebug TFinalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} # A ref type can have a finalizer that is called before the object's @@ -595,9 +595,15 @@ proc scan(s: PCell) = else: s.setColor(rcWhite) forAllChildren(s, waScan) - + 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) forAllChildren(s, waCollectWhite) freeCyclicCell(gch, s) @@ -648,6 +654,28 @@ when useMarkForDebug or useBackupGc: if objStart != nil: 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) = if p == nil: return var c: PCell = usrToCell(p) @@ -690,6 +718,7 @@ proc doOperation(p: pointer, op: TWalkOp) = of waMarkPrecise: when useMarkForDebug or useBackupGc: add(gch.tempStack, c) + #of waDebug: debugGraph(c) proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, TWalkOp(op)) @@ -702,7 +731,6 @@ when useMarkForDebug or useBackupGc: proc collectRoots(gch: var TGcHeap) = for s in elements(gch.cycleRoots): - excl(gch.cycleRoots, s) collectWhite(s) proc collectCycles(gch: var TGcHeap) = diff --git a/tests/gc/closureleak.nim b/tests/gc/closureleak.nim index 38ee1250a..1c39f43d5 100644 --- a/tests/gc/closureleak.nim +++ b/tests/gc/closureleak.nim @@ -7,7 +7,7 @@ from strutils import join type TFoo * = object id: int - func: proc(){.closure.} + fn: proc(){.closure.} var foo_counter = 0 var alive_foos = newseq[int](0) @@ -26,7 +26,7 @@ for i in 0 .. <10: for i in 0 .. <10: let f = newFoo() - f.func = proc = + f.fn = proc = echo f.id GC_fullcollect() diff --git a/tests/gc/cyclecollector.nim b/tests/gc/cyclecollector.nim new file mode 100644 index 000000000..46fed6c45 --- /dev/null +++ b/tests/gc/cyclecollector.nim @@ -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() diff --git a/tests/gc/gctest.nim b/tests/gc/gctest.nim index 27134d7dd..2213a83ac 100644 --- a/tests/gc/gctest.nim +++ b/tests/gc/gctest.nim @@ -196,7 +196,8 @@ write(stdout, "starting main...\n") main() GC_fullCollect() +# the M&S GC fails with this call and it's unclear why. Definitely something +# we need to fix! GC_fullCollect() writeln(stdout, GC_getStatistics()) write(stdout, "finished\n") - diff --git a/tests/testament/categories.nim b/tests/testament/categories.nim index ae9905cde..7c7d71aa2 100644 --- a/tests/testament/categories.nim +++ b/tests/testament/categories.nim @@ -107,12 +107,15 @@ proc dllTests(r: var TResults, cat: Category, options: string) = # ------------------------------ GC tests ------------------------------------- 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 & " -d:release", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & " -d:release -d:useRealtimeGC", cat, actionRun) + + template test(filename: expr): stmt = + testWithoutMs filename testSpec r, makeTest("tests/gc" / filename, options & " --gc:markAndSweep", cat, actionRun) testSpec r, makeTest("tests/gc" / filename, options & @@ -124,13 +127,15 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "gctest" test "gcleak3" test "gcleak4" - test "gcleak5" + # Disabled because it works and takes too long to run: + #test "gcleak5" test "weakrefs" test "cycleleak" test "closureleak" - test "refarrayleak" - test "stackrefleak" + testWithoutMs "refarrayleak" + test "stackrefleak" + test "cyclecollector" # ------------------------- threading tests ----------------------------------- |