more ORC bugfixes (#15355)
* introduced --define:nimArcIds * ORC: bugfixes
This commit is contained in:
parent
59b7857167
commit
d19316bbb9
4 changed files with 82 additions and 9 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
rootsThreshold = defaultThreshold
|
when defined(nimAdaptiveOrc):
|
||||||
|
rootsThreshold = max(rootsThreshold div 2, 16)
|
||||||
|
else:
|
||||||
|
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.} =
|
||||||
|
|
|
||||||
|
|
@ -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
46
tests/arc/tasyncleak3.nim
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue