ARC: cycle detector (#12823)
* first implementation of the =trace and =dispose hooks for the cycle collector * a cycle collector for ARC: progress * manual: the .acyclic pragma is a thing once again * gcbench: adaptations for --gc:arc * enable valgrind tests for the strutils tests * testament: better valgrind support * ARC refactoring: growable jumpstacks * ARC cycle detector: non-recursive algorithm * moved and renamed core/ files back to system/ * refactoring: --gc:arc vs --gc:orc since 'orc' is even more experimental and we want to ship --gc:arc soonish
This commit is contained in:
parent
5848f0042c
commit
83a736a34a
45 changed files with 939 additions and 229 deletions
|
|
@ -1,6 +1,6 @@
|
|||
discard """
|
||||
output: '''leak: true'''
|
||||
cmd: '''nim c --gc:arc $file'''
|
||||
output: '''leak: false'''
|
||||
cmd: '''nim c --gc:orc $file'''
|
||||
"""
|
||||
|
||||
type
|
||||
|
|
@ -19,11 +19,8 @@ proc addX(x: T; child: T) =
|
|||
proc main(rootName: string) =
|
||||
var root = create()
|
||||
root.data = rootName
|
||||
# this implies we do the refcounting wrong. We should leak memory here
|
||||
# and not create a destruction cycle:
|
||||
root.addX root
|
||||
|
||||
let mem = getOccupiedMem()
|
||||
main("yeah")
|
||||
# since we created a retain cycle, we MUST leak memory here:
|
||||
echo "leak: ", getOccupiedMem() - mem > 0
|
||||
|
|
|
|||
70
tests/destructor/tarctypesections.nim
Normal file
70
tests/destructor/tarctypesections.nim
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
discard """
|
||||
output: "MEM 0"
|
||||
cmd: "nim c --gc:arc $file"
|
||||
"""
|
||||
|
||||
type
|
||||
RefNode = ref object
|
||||
le, ri: RefNode
|
||||
name: char
|
||||
|
||||
proc edge0(a, b: RefNode) =
|
||||
if a.le == nil: a.le = b
|
||||
else: a.ri = b
|
||||
|
||||
proc createNode0(name: char): RefNode =
|
||||
new result
|
||||
result.name = name
|
||||
|
||||
proc main0 =
|
||||
let r = createNode0('R')
|
||||
let c = createNode0('C')
|
||||
c.edge0 r
|
||||
|
||||
|
||||
type
|
||||
NodeDesc = object
|
||||
le, ri: Node
|
||||
name: char
|
||||
Node = ref NodeDesc
|
||||
|
||||
proc edge(a, b: Node) =
|
||||
if a.le == nil: a.le = b
|
||||
else: a.ri = b
|
||||
|
||||
proc createNode(name: char): Node =
|
||||
new result
|
||||
result.name = name
|
||||
|
||||
proc main =
|
||||
let r = createNode('R')
|
||||
let c = createNode('C')
|
||||
c.edge r
|
||||
|
||||
|
||||
type
|
||||
NodeB = ref NodeBo
|
||||
NodeBo = object
|
||||
le, ri: NodeB
|
||||
name: char
|
||||
|
||||
proc edge(a, b: NodeB) =
|
||||
if a.le == nil: a.le = b
|
||||
else: a.ri = b
|
||||
|
||||
proc createNodeB(name: char): NodeB =
|
||||
new result
|
||||
result.name = name
|
||||
|
||||
|
||||
proc mainB =
|
||||
let r = createNodeB('R')
|
||||
let c = createNodeB('C')
|
||||
c.edge r
|
||||
|
||||
|
||||
let memB = getOccupiedMem()
|
||||
main0()
|
||||
main()
|
||||
mainB()
|
||||
echo "MEM ", getOccupiedMem() - memB
|
||||
54
tests/destructor/tasync_prototype_cyclic.nim
Normal file
54
tests/destructor/tasync_prototype_cyclic.nim
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
discard """
|
||||
output: '''asdas
|
||||
processClient end
|
||||
false
|
||||
MEMORY 0
|
||||
'''
|
||||
cmd: '''nim c --gc:orc $file'''
|
||||
"""
|
||||
|
||||
type
|
||||
PAsyncHttpServer = ref object
|
||||
value: string
|
||||
PFutureBase = ref object
|
||||
callback: proc () {.closure.}
|
||||
value: string
|
||||
failed: bool
|
||||
|
||||
proc accept(server: PAsyncHttpServer): PFutureBase =
|
||||
new(result)
|
||||
result.callback = proc () =
|
||||
discard
|
||||
server.value = "hahaha"
|
||||
|
||||
proc processClient(): PFutureBase =
|
||||
new(result)
|
||||
|
||||
proc serve(server: PAsyncHttpServer): PFutureBase =
|
||||
iterator serveIter(): PFutureBase {.closure.} =
|
||||
echo server.value
|
||||
while true:
|
||||
var acceptAddrFut = server.accept()
|
||||
yield acceptAddrFut
|
||||
var fut = acceptAddrFut.value
|
||||
|
||||
var f = processClient()
|
||||
when true:
|
||||
f.callback =
|
||||
proc () =
|
||||
echo("processClient end")
|
||||
echo(f.failed)
|
||||
yield f
|
||||
var x = serveIter
|
||||
for i in 0 .. 1:
|
||||
result = x()
|
||||
if result.callback != nil:
|
||||
result.callback()
|
||||
|
||||
let mem = getOccupiedMem()
|
||||
|
||||
proc main =
|
||||
discard serve(PAsyncHttpServer(value: "asdas"))
|
||||
|
||||
main()
|
||||
echo "MEMORY ", getOccupiedMem() - mem
|
||||
|
|
@ -4,7 +4,7 @@ discard """
|
|||
3 3 alloc/dealloc pairs: 0'''
|
||||
"""
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
import random
|
||||
|
|
|
|||
53
tests/destructor/tcycle1.nim
Normal file
53
tests/destructor/tcycle1.nim
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
discard """
|
||||
output: "MEM 0"
|
||||
cmd: "nim c --gc:orc $file"
|
||||
"""
|
||||
|
||||
type
|
||||
Node = ref object of RootObj
|
||||
le, ri: Node
|
||||
name: char
|
||||
|
||||
proc edge(a, b: Node) =
|
||||
if a.le == nil: a.le = b
|
||||
else: a.ri = b
|
||||
|
||||
proc createNode(name: char): Node =
|
||||
new result
|
||||
result.name = name
|
||||
|
||||
#[
|
||||
|
||||
+---------+ +------+
|
||||
| | | |
|
||||
| A +----->+ <------+-------------+
|
||||
+--+------+ | | | |
|
||||
| | | | C |
|
||||
| | R | | |
|
||||
+--v------+ | | +-------------+
|
||||
| | | | ^
|
||||
| B <------+ | |
|
||||
| | | +--------+
|
||||
+---------+ | |
|
||||
+------+
|
||||
|
||||
]#
|
||||
|
||||
proc main =
|
||||
let a = createNode('A')
|
||||
let b = createNode('B')
|
||||
let r = createNode('R')
|
||||
let c = createNode('C')
|
||||
|
||||
a.edge b
|
||||
a.edge r
|
||||
|
||||
r.edge b
|
||||
r.edge c
|
||||
|
||||
c.edge r
|
||||
|
||||
|
||||
let mem = getOccupiedMem()
|
||||
main()
|
||||
echo "MEM ", getOccupiedMem() - mem
|
||||
18
tests/destructor/tcycle2.nim
Normal file
18
tests/destructor/tcycle2.nim
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
discard """
|
||||
output: "MEM 0"
|
||||
cmd: "nim c --gc:orc $file"
|
||||
"""
|
||||
|
||||
type
|
||||
Node = ref object
|
||||
kids: seq[Node]
|
||||
data: string
|
||||
|
||||
proc main(x: int) =
|
||||
var n = Node(kids: @[], data: "3" & $x)
|
||||
let m = n
|
||||
n.kids.add m
|
||||
|
||||
let mem = getOccupiedMem()
|
||||
main(90)
|
||||
echo "MEM ", getOccupiedMem() - mem
|
||||
64
tests/destructor/tcycle3.nim
Normal file
64
tests/destructor/tcycle3.nim
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
discard """
|
||||
output: '''BEGIN
|
||||
END
|
||||
END 2
|
||||
0'''
|
||||
cmd: '''nim c --gc:orc $file'''
|
||||
"""
|
||||
|
||||
# extracted from thavlak.nim
|
||||
|
||||
type
|
||||
BasicBlock = ref object
|
||||
inEdges: seq[BasicBlock]
|
||||
outEdges: seq[BasicBlock]
|
||||
name: int
|
||||
|
||||
proc newBasicBlock(name: int): BasicBlock =
|
||||
result = BasicBlock(
|
||||
inEdges: newSeq[BasicBlock](),
|
||||
outEdges: newSeq[BasicBlock](),
|
||||
name: name
|
||||
)
|
||||
|
||||
type
|
||||
Cfg = object
|
||||
basicBlockMap: seq[BasicBlock]
|
||||
startNode: BasicBlock
|
||||
|
||||
proc newCfg(): Cfg =
|
||||
result = Cfg(
|
||||
basicBlockMap: newSeq[BasicBlock](),
|
||||
startNode: nil)
|
||||
|
||||
proc createNode(cfg: var Cfg, name: int): BasicBlock =
|
||||
if name < cfg.basicBlockMap.len:
|
||||
result = cfg.basicBlockMap[name]
|
||||
else:
|
||||
result = newBasicBlock(name)
|
||||
cfg.basicBlockMap.setLen name+1
|
||||
cfg.basicBlockMap[name] = result
|
||||
|
||||
proc newBasicBlockEdge(cfg: var Cfg, fromName, toName: int) =
|
||||
echo "BEGIN"
|
||||
let fr = cfg.createNode(fromName)
|
||||
let to = cfg.createNode(toName)
|
||||
|
||||
fr.outEdges.add(to)
|
||||
to.inEdges.add(fr)
|
||||
|
||||
proc run(cfg: var Cfg) =
|
||||
cfg.startNode = cfg.createNode(0) # RC = 2
|
||||
newBasicBlockEdge(cfg, 0, 1) #
|
||||
echo "END"
|
||||
|
||||
discard cfg.createNode(1)
|
||||
|
||||
proc main =
|
||||
var c = newCfg()
|
||||
c.run
|
||||
echo "END 2"
|
||||
|
||||
let mem = getOccupiedMem()
|
||||
main()
|
||||
echo getOccupiedMem() - mem
|
||||
|
|
@ -13,7 +13,7 @@ true
|
|||
41 41'''
|
||||
"""
|
||||
|
||||
import allocators
|
||||
import system / allocators
|
||||
include system / ansi_c
|
||||
|
||||
proc main =
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ test
|
|||
'''
|
||||
"""
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
import tables
|
||||
|
|
@ -132,5 +132,5 @@ proc xx(xml: string): MyObject =
|
|||
result.x = xml
|
||||
defer: echo stream
|
||||
|
||||
|
||||
|
||||
discard xx("test")
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
discard """
|
||||
cmd: '''nim c --newruntime $file'''
|
||||
valgrind: true
|
||||
cmd: '''nim c --newruntime -d:useMalloc $file'''
|
||||
output: '''422 422'''
|
||||
"""
|
||||
|
||||
import strutils, os, std / wordwrap
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
# bug #11004
|
||||
|
|
|
|||
26
tests/destructor/tselect.nim
Normal file
26
tests/destructor/tselect.nim
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
discard """
|
||||
output: '''abcsuffix
|
||||
xyzsuffix'''
|
||||
cmd: '''nim c --gc:arc $file'''
|
||||
"""
|
||||
|
||||
proc select(cond: bool; a, b: sink string): string =
|
||||
if cond:
|
||||
result = a # moves a into result
|
||||
else:
|
||||
result = b # moves b into result
|
||||
|
||||
proc test(param: string; cond: bool) =
|
||||
var x = "abc" & param
|
||||
var y = "xyz" & param
|
||||
|
||||
# possible self-assignment:
|
||||
x = select(cond, x, y)
|
||||
|
||||
echo x
|
||||
# 'select' must communicate what parameter has been
|
||||
# consumed. We cannot simply generate:
|
||||
# (select(...); wasMoved(x); wasMoved(y))
|
||||
|
||||
test("suffix", true)
|
||||
test("suffix", false)
|
||||
|
|
@ -8,7 +8,7 @@ hello
|
|||
2 2 alloc/dealloc pairs: 0'''
|
||||
"""
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
proc main(): owned(proc()) =
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ discard """
|
|||
line: 49
|
||||
"""
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ discard """
|
|||
|
||||
import strutils, math
|
||||
import system / ansi_c
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
|
||||
proc mainA =
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ clicked!
|
|||
1 1 alloc/dealloc pairs: 0'''
|
||||
"""
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
type
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ clicked!
|
|||
6 6 alloc/dealloc pairs: 0'''
|
||||
"""
|
||||
|
||||
import core / allocators
|
||||
import system / allocators
|
||||
import system / ansi_c
|
||||
|
||||
type
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue