more ORC bugfixes (#15355)

* introduced --define:nimArcIds

* ORC: bugfixes
This commit is contained in:
Andreas Rumpf 2020-09-18 11:55:58 +02:00 • committed by GitHub
commit d19316bbb9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 82 additions and 9 deletions

View file

@ -54,7 +54,7 @@ type
when defined(gcOrc): when defined(gcOrc):
rootIdx: int # thanks to this we can delete potential cycle roots rootIdx: int # thanks to this we can delete potential cycle roots
# in O(1) without doubly linked lists # in O(1) without doubly linked lists
when defined(nimArcDebug): when defined(nimArcDebug) or defined(nimArcIds):
refId: int refId: int
Cell = ptr RefHeader Cell = ptr RefHeader
@ -72,6 +72,8 @@ when defined(nimArcDebug):
var gRefId: int var gRefId: int
var freedCells: CellSet var freedCells: CellSet
elif defined(nimArcIds):
var gRefId: int
proc nimNewObj(size: int): pointer {.compilerRtl.} = proc nimNewObj(size: int): pointer {.compilerRtl.} =
let s = size + sizeof(RefHeader) let s = size + sizeof(RefHeader)
@ -85,7 +87,7 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} =
result = allocShared0(s) +! sizeof(RefHeader) result = allocShared0(s) +! sizeof(RefHeader)
else: else:
result = alloc0(s) +! sizeof(RefHeader) result = alloc0(s) +! sizeof(RefHeader)
when defined(nimArcDebug): when defined(nimArcDebug) or defined(nimArcIds):
head(result).refId = gRefId head(result).refId = gRefId
atomicInc gRefId atomicInc gRefId
when traceCollector: when traceCollector:

View file

@ -41,6 +41,7 @@ proc nimIncRefCyclic(p: pointer) {.compilerRtl, inl.} =
let h = head(p) let h = head(p)
inc h.rc, rcIncrement inc h.rc, rcIncrement
#h.setColor colPurple # mark as potential cycle! #h.setColor colPurple # mark as potential cycle!
h.setColor colBlack
const const
useJumpStack = false # for thavlak the jump stack doesn't improve the performance at all useJumpStack = false # for thavlak the jump stack doesn't improve the performance at all
@ -101,6 +102,11 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} =
var j = cast[ptr GcEnv](env) var j = cast[ptr GcEnv](env)
j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[]) j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[])
template orcAssert(cond, msg) =
if not cond:
cfprintf(cstderr, "[Bug!] %s\n", msg)
quit 1
var var
roots {.threadvar.}: CellSeq roots {.threadvar.}: CellSeq
@ -115,6 +121,11 @@ proc unregisterCycle(s: Cell) =
roots.d[idx][0].rootIdx = idx roots.d[idx][0].rootIdx = idx
dec roots.len dec roots.len
when false:
proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) =
cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld\n",
msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color)
proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
#[ #[
proc scanBlack(s: Cell) = proc scanBlack(s: Cell) =
@ -126,14 +137,17 @@ proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
]# ]#
debug "scanBlack", s debug "scanBlack", s
s.setColor colBlack s.setColor colBlack
let until = j.traceStack.len
trace(s, desc, j) trace(s, desc, j)
while j.traceStack.len > 0: #writeCell("root still alive", s, desc)
while j.traceStack.len > until:
let (t, desc) = j.traceStack.pop() let (t, desc) = j.traceStack.pop()
inc t.rc, rcIncrement inc t.rc, rcIncrement
debug "incRef", t debug "incRef", t
if t.color != colBlack: if t.color != colBlack:
t.setColor colBlack t.setColor colBlack
trace(t, desc, j) trace(t, desc, j)
#writeCell("child still alive", t, desc)
proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
#[ #[
@ -148,6 +162,7 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
if s.color != colGray: if s.color != colGray:
s.setColor colGray s.setColor colGray
inc j.touched inc j.touched
orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty")
trace(s, desc, j) trace(s, desc, j)
while j.traceStack.len > 0: while j.traceStack.len > 0:
let (t, desc) = j.traceStack.pop() let (t, desc) = j.traceStack.pop()
@ -195,6 +210,7 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
when traceCollector: when traceCollector:
cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift) cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift)
orcAssert(j.traceStack.len == 0, "scan: trace stack not empty")
s.setColor(colWhite) s.setColor(colWhite)
trace(s, desc, j) trace(s, desc, j)
while j.traceStack.len > 0: while j.traceStack.len > 0:
@ -236,6 +252,8 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) =
free(s) # watch out, a bug here! free(s) # watch out, a bug here!
]# ]#
if s.color == colWhite and (s.rc and isCycleCandidate) == 0: if s.color == colWhite and (s.rc and isCycleCandidate) == 0:
orcAssert(j.traceStack.len == 0, "collectWhite: trace stack not empty")
s.setColor(colBlack) s.setColor(colBlack)
j.toFree.add(s, desc) j.toFree.add(s, desc)
trace(s, desc, j) trace(s, desc, j)
@ -261,6 +279,7 @@ proc collectCyclesBacon(j: var GcEnv) =
collectWhite(s) collectWhite(s)
]# ]#
for i in 0 ..< roots.len: for i in 0 ..< roots.len:
#writeCell("root", roots.d[i][0], roots.d[i][1])
markGray(roots.d[i][0], roots.d[i][1], j) markGray(roots.d[i][0], roots.d[i][1], j)
for i in 0 ..< roots.len: for i in 0 ..< roots.len:
scan(roots.d[i][0], roots.d[i][1], j) scan(roots.d[i][0], roots.d[i][1], j)
@ -279,7 +298,7 @@ proc collectCyclesBacon(j: var GcEnv) =
#roots.len = 0 #roots.len = 0
const const
defaultThreshold = 10_000 defaultThreshold = when defined(nimAdaptiveOrc): 128 else: 10_000
var var
rootsThreshold = defaultThreshold rootsThreshold = defaultThreshold
@ -309,11 +328,16 @@ proc collectCycles() =
# we're effective when we collected 50% or more of the nodes # we're effective when we collected 50% or more of the nodes
# we touched. If we're effective, we can reset the threshold: # we touched. If we're effective, we can reset the threshold:
if j.freed * 2 >= j.touched: if j.freed * 2 >= j.touched:
when defined(nimAdaptiveOrc):
rootsThreshold = max(rootsThreshold div 2, 16)
else:
rootsThreshold = defaultThreshold rootsThreshold = defaultThreshold
#cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold)
elif rootsThreshold < high(int) div 4: elif rootsThreshold < high(int) div 4:
rootsThreshold = rootsThreshold * 3 div 2 rootsThreshold = rootsThreshold * 3 div 2
when false: when false:
cfprintf(cstderr, "[collectCycles] freed %ld new threshold %ld\n", j.freed, rootsThreshold) cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld\n", j.freed, rootsThreshold, j.touched,
getOccupiedMem())
proc registerCycle(s: Cell; desc: PNimTypeV2) = proc registerCycle(s: Cell; desc: PNimTypeV2) =
if roots.len >= rootsThreshold: if roots.len >= rootsThreshold:
@ -328,10 +352,10 @@ proc GC_fullCollect* =
## collector. ## collector.
collectCycles() collectCycles()
proc GC_enableMarkAndSweep() = proc GC_enableMarkAndSweep*() =
rootsThreshold = defaultThreshold rootsThreshold = defaultThreshold
proc GC_disableMarkAndSweep() = proc GC_disableMarkAndSweep*() =
rootsThreshold = high(int) rootsThreshold = high(int)
proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} =
@ -345,6 +369,7 @@ proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.
#s.setColor colGreen # XXX This is wrong! #s.setColor colGreen # XXX This is wrong!
if (s.rc and isCycleCandidate) == 0: if (s.rc and isCycleCandidate) == 0:
s.rc = s.rc or isCycleCandidate s.rc = s.rc or isCycleCandidate
s.setColor colBlack
registerCycle(s, desc) registerCycle(s, desc)
proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} =

View file

@ -1,5 +1,5 @@
discard """ discard """
outputsub: "(allocCount: 4016, deallocCount: 4010)" outputsub: "(allocCount: 4016, deallocCount: 4014)"
cmd: "nim c --gc:orc -d:nimAllocStats $file" cmd: "nim c --gc:orc -d:nimAllocStats $file"
""" """

46
tests/arc/tasyncleak3.nim Normal file
View file

@ -0,0 +1,46 @@
discard """
cmd: "nim c --gc:orc $file"
output: "true"
"""
import strutils
type
FutureBase* = ref object of RootObj ## Untyped future.
finished: bool
stackTrace: seq[StackTraceEntry] ## For debugging purposes only.
proc newFuture*(): FutureBase =
new(result)
result.finished = false
result.stackTrace = getStackTraceEntries()
type
PDispatcher {.acyclic.} = ref object
var gDisp: PDispatcher
new gDisp
proc testCompletion(): FutureBase =
var retFuture = newFuture()
iterator testCompletionIter(): FutureBase {.closure.} =
retFuture.finished = true
when not defined(nobug):
let disp = gDisp # even worse memory consumption this way...
var nameIterVar = testCompletionIter
proc testCompletionNimAsyncContinue() {.closure.} =
if not nameIterVar.finished:
discard nameIterVar()
testCompletionNimAsyncContinue()
return retFuture
proc main =
for i in 0..10_000:
discard testCompletion()
main()
GC_fullCollect()
echo getOccupiedMem() < 1024