scoped memory management (#14790)
* fixes the regressions * closes #13936 * scope based memory management implemented * enabled tcontrolflow.nim test case * final cleanups
This commit is contained in:
parent
695154970d
commit
1854d29781
17 changed files with 611 additions and 477 deletions
|
|
@ -173,3 +173,39 @@ proc bug14495 =
|
|||
echo o[]
|
||||
|
||||
bug14495()
|
||||
|
||||
when false:
|
||||
# bug #14396
|
||||
type
|
||||
Spinny = ref object
|
||||
t: ref int
|
||||
text: string
|
||||
|
||||
proc newSpinny*(): Spinny =
|
||||
Spinny(t: new(int), text: "hello")
|
||||
|
||||
proc spinnyLoop(x: ref int, spinny: sink Spinny) =
|
||||
echo x[]
|
||||
|
||||
proc start*(spinny: sink Spinny) =
|
||||
spinnyLoop(spinny.t, spinny)
|
||||
|
||||
var spinner1 = newSpinny()
|
||||
spinner1.start()
|
||||
|
||||
# bug #14345
|
||||
|
||||
type
|
||||
SimpleLoopB = ref object
|
||||
children: seq[SimpleLoopB]
|
||||
parent: SimpleLoopB
|
||||
|
||||
proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) =
|
||||
self.children.add loop
|
||||
|
||||
proc setParent(self: SimpleLoopB, parent: SimpleLoopB) =
|
||||
self.parent = parent
|
||||
self.parent.addChildLoop(self)
|
||||
|
||||
var l = SimpleLoopB()
|
||||
l.setParent(l)
|
||||
|
|
|
|||
|
|
@ -11,15 +11,13 @@ begin true
|
|||
if
|
||||
end true
|
||||
7
|
||||
##index 2 not in 0 .. 1##
|
||||
'''
|
||||
cmd: "nim c --gc:arc -d:danger $file"
|
||||
disabled: "true"
|
||||
"""
|
||||
# we use the -d:danger switch to detect uninitialized stack
|
||||
# slots more reliably (there shouldn't be any, of course).
|
||||
|
||||
# XXX Enable once scope based destruction works!
|
||||
|
||||
type
|
||||
Foo = object
|
||||
id: int
|
||||
|
|
@ -68,12 +66,25 @@ proc run(data: Control) =
|
|||
evt.control = data
|
||||
if evt.button == 1:
|
||||
discard
|
||||
else:
|
||||
else:
|
||||
return
|
||||
|
||||
|
||||
echo data.x
|
||||
|
||||
var c = Control(x: 7)
|
||||
|
||||
run(c)
|
||||
|
||||
proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} =
|
||||
var buf = newStringOfCap(200)
|
||||
add(buf, "##")
|
||||
add(buf, message)
|
||||
add(buf, "##")
|
||||
echo buf
|
||||
|
||||
proc ifexpr(i, a, b: int) {.compilerproc, noinline.} =
|
||||
sysFatal(IndexDefect,
|
||||
if b < a: "index out of bounds, the container is empty"
|
||||
else: "index " & $i & " not in " & $a & " .. " & $b)
|
||||
|
||||
ifexpr(2, 0, 1)
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ iterator combinations[T](s: openarray[T], k: int): seq[T] =
|
|||
break
|
||||
|
||||
type
|
||||
UndefEx = object of Exception
|
||||
UndefEx = object of ValueError
|
||||
|
||||
proc main2 =
|
||||
var delayedSyms = @[1, 2, 3]
|
||||
|
|
@ -318,10 +318,10 @@ proc `=sink`(dest: var O2, src: O2) =
|
|||
|
||||
var testSeq: O2
|
||||
|
||||
proc Update(): void =
|
||||
proc update() =
|
||||
# testSeq.add(0) # uncommenting this line fixes the leak
|
||||
testSeq = O2(s: @[])
|
||||
testSeq.s.add(0)
|
||||
|
||||
for i in 1..3:
|
||||
Update()
|
||||
update()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ type
|
|||
p: int
|
||||
MyObjRef = ref MyObj
|
||||
|
||||
proc `=destroy`(x: var MyObj) =
|
||||
proc `=destroy`(x: var MyObj) =
|
||||
if x.p != 0:
|
||||
echo "destroyed"
|
||||
|
||||
|
|
@ -48,16 +48,16 @@ proc thread5(x: sink MyObjRef): MyObjRef =
|
|||
os.sleep(1000)
|
||||
result = x
|
||||
|
||||
proc ref_forwarding_test =
|
||||
proc ref_forwarding_test =
|
||||
var x = new(MyObj)
|
||||
x[].p = 2
|
||||
var y = spawn thread4(x)
|
||||
|
||||
proc ref_sink_forwarding_test =
|
||||
|
||||
proc ref_sink_forwarding_test =
|
||||
var x = new(MyObj)
|
||||
x[].p = 2
|
||||
var y = spawn thread5(x)
|
||||
|
||||
ref_forwarding_test()
|
||||
ref_sink_forwarding_test()
|
||||
ref_sink_forwarding_test()
|
||||
sync()
|
||||
|
|
|
|||
154
tests/arc/tweave.nim
Normal file
154
tests/arc/tweave.nim
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
discard """
|
||||
outputsub: '''Success'''
|
||||
cmd: '''nim c --gc:arc --threads:on $file'''
|
||||
disabled: "bsd"
|
||||
"""
|
||||
|
||||
# bug #13936
|
||||
|
||||
import std/atomics
|
||||
|
||||
const MemBlockSize = 256
|
||||
|
||||
type
|
||||
ChannelSPSCSingle* = object
|
||||
full{.align: 128.}: Atomic[bool]
|
||||
itemSize*: uint8
|
||||
buffer*{.align: 8.}: UncheckedArray[byte]
|
||||
|
||||
proc `=`(
|
||||
dest: var ChannelSPSCSingle,
|
||||
source: ChannelSPSCSingle
|
||||
) {.error: "A channel cannot be copied".}
|
||||
|
||||
proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} =
|
||||
## If ChannelSPSCSingle is used intrusive another data structure
|
||||
## be aware that it should be the last part due to ending by UncheckedArray
|
||||
## Also due to 128 bytes padding, it automatically takes half
|
||||
## of the default MemBlockSize
|
||||
assert itemsize.int in 0 .. int high(uint8)
|
||||
assert itemSize.int +
|
||||
sizeof(chan.itemsize) +
|
||||
sizeof(chan.full) < MemBlockSize
|
||||
|
||||
chan.itemSize = uint8 itemsize
|
||||
chan.full.store(false, moRelaxed)
|
||||
|
||||
func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} =
|
||||
not chan.full.load(moAcquire)
|
||||
|
||||
func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} =
|
||||
## Try receiving the item buffered in the channel
|
||||
## Returns true if successful (channel was not empty)
|
||||
##
|
||||
## ⚠ Use only in the consumer thread that reads from the channel.
|
||||
assert (sizeof(T) == chan.itemsize.int) or
|
||||
# Support dummy object
|
||||
(sizeof(T) == 0 and chan.itemsize == 1)
|
||||
|
||||
let full = chan.full.load(moAcquire)
|
||||
if not full:
|
||||
return false
|
||||
dst = cast[ptr T](chan.buffer.addr)[]
|
||||
chan.full.store(false, moRelease)
|
||||
return true
|
||||
|
||||
func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} =
|
||||
## Try sending an item into the channel
|
||||
## Reurns true if successful (channel was empty)
|
||||
##
|
||||
## ⚠ Use only in the producer thread that writes from the channel.
|
||||
assert (sizeof(T) == chan.itemsize.int) or
|
||||
# Support dummy object
|
||||
(sizeof(T) == 0 and chan.itemsize == 1)
|
||||
|
||||
let full = chan.full.load(moAcquire)
|
||||
if full:
|
||||
return false
|
||||
cast[ptr T](chan.buffer.addr)[] = src
|
||||
chan.full.store(true, moRelease)
|
||||
return true
|
||||
|
||||
# Sanity checks
|
||||
# ------------------------------------------------------------------------------
|
||||
when isMainModule:
|
||||
|
||||
when not compileOption("threads"):
|
||||
{.error: "This requires --threads:on compilation flag".}
|
||||
|
||||
template sendLoop[T](chan: var ChannelSPSCSingle,
|
||||
data: sink T,
|
||||
body: untyped): untyped =
|
||||
while not chan.trySend(data):
|
||||
body
|
||||
|
||||
template recvLoop[T](chan: var ChannelSPSCSingle,
|
||||
data: var T,
|
||||
body: untyped): untyped =
|
||||
while not chan.tryRecv(data):
|
||||
body
|
||||
|
||||
type
|
||||
ThreadArgs = object
|
||||
ID: WorkerKind
|
||||
chan: ptr ChannelSPSCSingle
|
||||
|
||||
WorkerKind = enum
|
||||
Sender
|
||||
Receiver
|
||||
|
||||
template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} =
|
||||
if args.ID == id:
|
||||
body
|
||||
|
||||
proc thread_func(args: ThreadArgs) =
|
||||
|
||||
# Worker RECEIVER:
|
||||
# ---------
|
||||
# <- chan
|
||||
# <- chan
|
||||
# <- chan
|
||||
#
|
||||
# Worker SENDER:
|
||||
# ---------
|
||||
# chan <- 42
|
||||
# chan <- 53
|
||||
# chan <- 64
|
||||
Worker(Receiver):
|
||||
var val: int
|
||||
for j in 0 ..< 10:
|
||||
args.chan[].recvLoop(val):
|
||||
# Busy loop, in prod we might want to yield the core/thread timeslice
|
||||
discard
|
||||
echo " Receiver got: ", val
|
||||
doAssert val == 42 + j*11
|
||||
|
||||
Worker(Sender):
|
||||
doAssert args.chan.full.load(moRelaxed) == false
|
||||
for j in 0 ..< 10:
|
||||
let val = 42 + j*11
|
||||
args.chan[].sendLoop(val):
|
||||
# Busy loop, in prod we might want to yield the core/thread timeslice
|
||||
discard
|
||||
echo "Sender sent: ", val
|
||||
|
||||
proc main() =
|
||||
echo "Testing if 2 threads can send data"
|
||||
echo "-----------------------------------"
|
||||
var threads: array[2, Thread[ThreadArgs]]
|
||||
|
||||
var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize))
|
||||
chan[].initialize(itemSize = sizeof(int))
|
||||
|
||||
createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan))
|
||||
createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan))
|
||||
|
||||
joinThread(threads[0])
|
||||
joinThread(threads[1])
|
||||
|
||||
freeShared(chan)
|
||||
|
||||
echo "-----------------------------------"
|
||||
echo "Success"
|
||||
|
||||
main()
|
||||
|
|
@ -1,8 +1,9 @@
|
|||
discard """
|
||||
output: '''
|
||||
hasPendingOperations: false
|
||||
triggerCount: 100
|
||||
triggerCount: 100
|
||||
'''
|
||||
disabled: "windows"
|
||||
"""
|
||||
|
||||
import asyncDispatch
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ discard """
|
|||
output: '''copied
|
||||
copied
|
||||
2
|
||||
destroyed
|
||||
destroyed
|
||||
copied
|
||||
copied
|
||||
2
|
||||
|
|
@ -37,9 +39,9 @@ proc test(a: range[0..1], arg: ObjWithDestructor) =
|
|||
inc iteration
|
||||
|
||||
case a
|
||||
of 0:
|
||||
assert false
|
||||
of 1:
|
||||
of 0:
|
||||
assert false
|
||||
of 1:
|
||||
echo b
|
||||
if iteration == 2:
|
||||
break
|
||||
|
|
|
|||
|
|
@ -1,27 +1,27 @@
|
|||
discard """
|
||||
output: '''----
|
||||
output: '''----1
|
||||
myobj constructed
|
||||
myobj destroyed
|
||||
----
|
||||
----2
|
||||
mygeneric1 constructed
|
||||
mygeneric1 destroyed
|
||||
----
|
||||
----3
|
||||
mygeneric2 constructed
|
||||
mygeneric2 destroyed
|
||||
myobj destroyed
|
||||
----
|
||||
----4
|
||||
mygeneric3 constructed
|
||||
mygeneric1 destroyed
|
||||
----
|
||||
----5
|
||||
mydistinctObj constructed
|
||||
myobj destroyed
|
||||
mygeneric2 destroyed
|
||||
------------------
|
||||
----
|
||||
----
|
||||
myobj destroyed
|
||||
------------------8
|
||||
mygeneric1 destroyed
|
||||
---
|
||||
----6
|
||||
myobj destroyed
|
||||
----7
|
||||
---9
|
||||
myobj destroyed
|
||||
myobj destroyed
|
||||
'''
|
||||
|
|
@ -114,19 +114,19 @@ proc mydistinctObj =
|
|||
|
||||
echo "mydistinctObj constructed"
|
||||
|
||||
echo "----"
|
||||
echo "----1"
|
||||
myobj()
|
||||
|
||||
echo "----"
|
||||
echo "----2"
|
||||
mygeneric1()
|
||||
|
||||
echo "----"
|
||||
echo "----3"
|
||||
mygeneric2[int](10)
|
||||
|
||||
echo "----"
|
||||
echo "----4"
|
||||
mygeneric3()
|
||||
|
||||
echo "----"
|
||||
echo "----5"
|
||||
mydistinctObj()
|
||||
|
||||
proc caseobj =
|
||||
|
|
@ -134,16 +134,16 @@ proc caseobj =
|
|||
var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
|
||||
|
||||
block:
|
||||
echo "----"
|
||||
echo "----6"
|
||||
var o2 = TCaseObj(kind: B, y: open())
|
||||
|
||||
block:
|
||||
echo "----"
|
||||
echo "----7"
|
||||
var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
|
||||
p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
|
||||
|
||||
|
||||
echo "------------------"
|
||||
echo "------------------8"
|
||||
caseobj()
|
||||
|
||||
proc caseobj_test_sink: TCaseObj =
|
||||
|
|
@ -153,7 +153,7 @@ proc caseobj_test_sink: TCaseObj =
|
|||
result = TCaseObj(kind: B, y: open())
|
||||
|
||||
|
||||
echo "---"
|
||||
echo "---9"
|
||||
discard caseobj_test_sink()
|
||||
|
||||
# issue #14315
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue