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
|
|
@ -644,7 +644,33 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) =
|
||||||
pl.add(~"];$n")
|
pl.add(~"];$n")
|
||||||
line(p, cpsStmts, pl)
|
line(p, cpsStmts, pl)
|
||||||
|
|
||||||
|
proc notYetAlive(n: PNode): bool {.inline.} =
|
||||||
|
let r = getRoot(n)
|
||||||
|
result = r != nil and r.loc.lode == nil
|
||||||
|
|
||||||
|
proc isInactiveDestructorCall(p: BProc, e: PNode): bool =
|
||||||
|
#[ Consider this example.
|
||||||
|
|
||||||
|
var :tmpD_3281815
|
||||||
|
try:
|
||||||
|
if true:
|
||||||
|
return
|
||||||
|
let args_3280013 =
|
||||||
|
wasMoved_3281816(:tmpD_3281815)
|
||||||
|
`=_3280036`(:tmpD_3281815, [1])
|
||||||
|
:tmpD_3281815
|
||||||
|
finally:
|
||||||
|
`=destroy_3280027`(args_3280013)
|
||||||
|
|
||||||
|
We want to return early but the 'finally' section is traversed before
|
||||||
|
the 'let args = ...' statement. We exploit this to generate better
|
||||||
|
code for 'return'. ]#
|
||||||
|
result = e.len == 2 and e[0].kind == nkSym and
|
||||||
|
e[0].sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr)
|
||||||
|
|
||||||
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
proc genCall(p: BProc, e: PNode, d: var TLoc) =
|
||||||
|
if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, e):
|
||||||
|
return
|
||||||
if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure:
|
||||||
genClosureCall(p, nil, e, d)
|
genClosureCall(p, nil, e, d)
|
||||||
elif e[0].kind == nkSym and sfInfixCall in e[0].sym.flags:
|
elif e[0].kind == nkSym and sfInfixCall in e[0].sym.flags:
|
||||||
|
|
|
||||||
|
|
@ -2066,10 +2066,14 @@ proc skipAddr(n: PNode): PNode =
|
||||||
|
|
||||||
proc genWasMoved(p: BProc; n: PNode) =
|
proc genWasMoved(p: BProc; n: PNode) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
initLocExpr(p, n[1].skipAddr, a)
|
let n1 = n[1].skipAddr
|
||||||
resetLoc(p, a)
|
if p.withinBlockLeaveActions > 0 and notYetAlive(n1):
|
||||||
#linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
discard
|
||||||
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
|
else:
|
||||||
|
initLocExpr(p, n1, a)
|
||||||
|
resetLoc(p, a)
|
||||||
|
#linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n",
|
||||||
|
# [addrLoc(p.config, a), getTypeDesc(p.module, a.t)])
|
||||||
|
|
||||||
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
proc genMove(p: BProc; n: PNode; d: var TLoc) =
|
||||||
var a: TLoc
|
var a: TLoc
|
||||||
|
|
@ -2593,10 +2597,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) =
|
||||||
else:
|
else:
|
||||||
putLocIntoDest(p, d, sym.loc)
|
putLocIntoDest(p, d, sym.loc)
|
||||||
of skTemp:
|
of skTemp:
|
||||||
if sym.loc.r == nil:
|
when false:
|
||||||
# we now support undeclared 'skTemp' variables for easier
|
# this is more harmful than helpful.
|
||||||
# transformations in other parts of the compiler:
|
if sym.loc.r == nil:
|
||||||
assignLocalVar(p, n)
|
# we now support undeclared 'skTemp' variables for easier
|
||||||
|
# transformations in other parts of the compiler:
|
||||||
|
assignLocalVar(p, n)
|
||||||
if sym.loc.r == nil or sym.loc.t == nil:
|
if sym.loc.r == nil or sym.loc.t == nil:
|
||||||
#echo "FAILED FOR PRCO ", p.prc.name.s
|
#echo "FAILED FOR PRCO ", p.prc.name.s
|
||||||
#echo renderTree(p.prc.ast, {renderIds})
|
#echo renderTree(p.prc.ast, {renderIds})
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
|
|
||||||
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
|
var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0)
|
||||||
|
|
||||||
|
inc p.withinBlockLeaveActions
|
||||||
for i in 1..howManyTrys:
|
for i in 1..howManyTrys:
|
||||||
let tryStmt = p.nestedTryStmts.pop
|
let tryStmt = p.nestedTryStmts.pop
|
||||||
if p.config.exc == excSetjmp:
|
if p.config.exc == excSetjmp:
|
||||||
|
|
@ -217,6 +218,8 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) =
|
||||||
if finallyStmt != nil:
|
if finallyStmt != nil:
|
||||||
genStmts(p, finallyStmt[0])
|
genStmts(p, finallyStmt[0])
|
||||||
|
|
||||||
|
dec p.withinBlockLeaveActions
|
||||||
|
|
||||||
# push old elements again:
|
# push old elements again:
|
||||||
for i in countdown(howManyTrys-1, 0):
|
for i in countdown(howManyTrys-1, 0):
|
||||||
p.nestedTryStmts.add(stack[i])
|
p.nestedTryStmts.add(stack[i])
|
||||||
|
|
@ -861,10 +864,10 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) =
|
||||||
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
|
genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n")
|
||||||
|
|
||||||
proc branchHasTooBigRange(b: PNode): bool =
|
proc branchHasTooBigRange(b: PNode): bool =
|
||||||
for i in 0..<b.len-1:
|
for it in b:
|
||||||
# last son is block
|
# last son is block
|
||||||
if (b[i].kind == nkRange) and
|
if (it.kind == nkRange) and
|
||||||
b[i][1].intVal - b[i][0].intVal > RangeExpandLimit:
|
it[1].intVal - it[0].intVal > RangeExpandLimit:
|
||||||
return true
|
return true
|
||||||
|
|
||||||
proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
|
proc ifSwitchSplitPoint(p: BProc, n: PNode): int =
|
||||||
|
|
@ -988,9 +991,14 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||||
p.nestedTryStmts.add((fin, false, 0.Natural))
|
p.nestedTryStmts.add((fin, false, 0.Natural))
|
||||||
|
|
||||||
startBlock(p, "try {$n")
|
if t.kind == nkHiddenTryStmt:
|
||||||
expr(p, t[0], d)
|
lineCg(p, cpsStmts, "try {$n", [])
|
||||||
endBlock(p)
|
expr(p, t[0], d)
|
||||||
|
lineCg(p, cpsStmts, "}$n", [])
|
||||||
|
else:
|
||||||
|
startBlock(p, "try {$n")
|
||||||
|
expr(p, t[0], d)
|
||||||
|
endBlock(p)
|
||||||
|
|
||||||
# First pass: handle Nim based exceptions:
|
# First pass: handle Nim based exceptions:
|
||||||
lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1])
|
lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1])
|
||||||
|
|
@ -1335,13 +1343,13 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) =
|
||||||
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint])
|
||||||
else:
|
else:
|
||||||
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint])
|
||||||
startBlock(p, "if ($1.status == 0) {$n", [safePoint])
|
lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint])
|
||||||
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
let fin = if t[^1].kind == nkFinally: t[^1] else: nil
|
||||||
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
|
p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural))
|
||||||
expr(p, t[0], d)
|
expr(p, t[0], d)
|
||||||
if not quirkyExceptions:
|
if not quirkyExceptions:
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||||
endBlock(p)
|
lineCg(p, cpsStmts, "}$n", [])
|
||||||
startBlock(p, "else {$n")
|
startBlock(p, "else {$n")
|
||||||
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
linefmt(p, cpsStmts, "#popSafePoint();$n", [])
|
||||||
genRestoreFrameAfterException(p)
|
genRestoreFrameAfterException(p)
|
||||||
|
|
|
||||||
|
|
@ -1561,7 +1561,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) =
|
||||||
if sfSystemModule in m.module.flags:
|
if sfSystemModule in m.module.flags:
|
||||||
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone:
|
||||||
g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
|
g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", []))
|
||||||
if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone:
|
if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}:
|
||||||
g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
|
g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []))
|
||||||
|
|
||||||
if m.s[cfsInitProc].len > 0:
|
if m.s[cfsInitProc].len > 0:
|
||||||
|
|
@ -1666,6 +1666,10 @@ proc genInitCode(m: BModule) =
|
||||||
writeSection(preInitProc, cpsInit, m.hcrOn)
|
writeSection(preInitProc, cpsInit, m.hcrOn)
|
||||||
writeSection(preInitProc, cpsStmts)
|
writeSection(preInitProc, cpsStmts)
|
||||||
prc.addf("}$N", [])
|
prc.addf("}$N", [])
|
||||||
|
when false:
|
||||||
|
m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals)
|
||||||
|
m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit)
|
||||||
|
m.initProc.blocks[0].sections[cpsStmts].prepend m.preInitProc.s(cpsStmts)
|
||||||
|
|
||||||
# add new scope for following code, because old vcc compiler need variable
|
# add new scope for following code, because old vcc compiler need variable
|
||||||
# be defined at the top of the block
|
# be defined at the top of the block
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ type
|
||||||
# requires 'T x = T()' to become 'T x; x = T()'
|
# requires 'T x = T()' to become 'T x; x = T()'
|
||||||
# (yes, C++ is weird like that)
|
# (yes, C++ is weird like that)
|
||||||
withinTryWithExcept*: int # required for goto based exception handling
|
withinTryWithExcept*: int # required for goto based exception handling
|
||||||
|
withinBlockLeaveActions*: int # complex to explain
|
||||||
sigConflicts*: CountTable[string]
|
sigConflicts*: CountTable[string]
|
||||||
|
|
||||||
TTypeSeq* = seq[PType]
|
TTypeSeq* = seq[PType]
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -229,8 +229,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode =
|
||||||
a[0][2] = newSymNode(symbol, a[0][2].info)
|
a[0][2] = newSymNode(symbol, a[0][2].info)
|
||||||
|
|
||||||
elif a.len == 1:
|
elif a.len == 1:
|
||||||
# count number of ``except: body`` blocks
|
# count number of ``except: body`` blocks
|
||||||
inc catchAllExcepts
|
inc catchAllExcepts
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# support ``except KeyError, ValueError, ... : body``
|
# support ``except KeyError, ValueError, ... : body``
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
## This module implements threadpool's ``spawn``.
|
## This module implements threadpool's ``spawn``.
|
||||||
|
|
||||||
import ast, types, idents, magicsys, msgs, options, modulegraphs,
|
import ast, types, idents, magicsys, msgs, options, modulegraphs,
|
||||||
lowerings, liftdestructors
|
lowerings, liftdestructors, renderer
|
||||||
from trees import getMagic, getRoot
|
from trees import getMagic, getRoot
|
||||||
|
|
||||||
proc callProc(a: PNode): PNode =
|
proc callProc(a: PNode): PNode =
|
||||||
|
|
@ -321,7 +321,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P
|
||||||
result = newNodeI(nkStmtList, n.info)
|
result = newNodeI(nkStmtList, n.info)
|
||||||
|
|
||||||
if n.kind notin nkCallKinds:
|
if n.kind notin nkCallKinds:
|
||||||
localError(g.config, n.info, "'spawn' takes a call expression")
|
localError(g.config, n.info, "'spawn' takes a call expression; got " & $n)
|
||||||
return
|
return
|
||||||
if optThreadAnalysis in g.config.globalOptions:
|
if optThreadAnalysis in g.config.globalOptions:
|
||||||
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}:
|
||||||
|
|
|
||||||
|
|
@ -283,8 +283,8 @@ Rewrite rules
|
||||||
around the complete routine body.
|
around the complete routine body.
|
||||||
2. The produced ``finally`` section is wrapped around the enclosing scope.
|
2. The produced ``finally`` section is wrapped around the enclosing scope.
|
||||||
|
|
||||||
The current implementation follows strategy (1). This means that resources are
|
The current implementation follows strategy (2). This means that resources are
|
||||||
not destroyed at the scope exit, but at the proc exit.
|
destroyed at the scope exit.
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,3 +173,39 @@ proc bug14495 =
|
||||||
echo o[]
|
echo o[]
|
||||||
|
|
||||||
bug14495()
|
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
|
if
|
||||||
end true
|
end true
|
||||||
7
|
7
|
||||||
|
##index 2 not in 0 .. 1##
|
||||||
'''
|
'''
|
||||||
cmd: "nim c --gc:arc -d:danger $file"
|
cmd: "nim c --gc:arc -d:danger $file"
|
||||||
disabled: "true"
|
|
||||||
"""
|
"""
|
||||||
# we use the -d:danger switch to detect uninitialized stack
|
# we use the -d:danger switch to detect uninitialized stack
|
||||||
# slots more reliably (there shouldn't be any, of course).
|
# slots more reliably (there shouldn't be any, of course).
|
||||||
|
|
||||||
# XXX Enable once scope based destruction works!
|
|
||||||
|
|
||||||
type
|
type
|
||||||
Foo = object
|
Foo = object
|
||||||
id: int
|
id: int
|
||||||
|
|
@ -68,12 +66,25 @@ proc run(data: Control) =
|
||||||
evt.control = data
|
evt.control = data
|
||||||
if evt.button == 1:
|
if evt.button == 1:
|
||||||
discard
|
discard
|
||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
echo data.x
|
echo data.x
|
||||||
|
|
||||||
var c = Control(x: 7)
|
var c = Control(x: 7)
|
||||||
|
|
||||||
run(c)
|
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
|
break
|
||||||
|
|
||||||
type
|
type
|
||||||
UndefEx = object of Exception
|
UndefEx = object of ValueError
|
||||||
|
|
||||||
proc main2 =
|
proc main2 =
|
||||||
var delayedSyms = @[1, 2, 3]
|
var delayedSyms = @[1, 2, 3]
|
||||||
|
|
@ -318,10 +318,10 @@ proc `=sink`(dest: var O2, src: O2) =
|
||||||
|
|
||||||
var testSeq: O2
|
var testSeq: O2
|
||||||
|
|
||||||
proc Update(): void =
|
proc update() =
|
||||||
# testSeq.add(0) # uncommenting this line fixes the leak
|
# testSeq.add(0) # uncommenting this line fixes the leak
|
||||||
testSeq = O2(s: @[])
|
testSeq = O2(s: @[])
|
||||||
testSeq.s.add(0)
|
testSeq.s.add(0)
|
||||||
|
|
||||||
for i in 1..3:
|
for i in 1..3:
|
||||||
Update()
|
update()
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ type
|
||||||
p: int
|
p: int
|
||||||
MyObjRef = ref MyObj
|
MyObjRef = ref MyObj
|
||||||
|
|
||||||
proc `=destroy`(x: var MyObj) =
|
proc `=destroy`(x: var MyObj) =
|
||||||
if x.p != 0:
|
if x.p != 0:
|
||||||
echo "destroyed"
|
echo "destroyed"
|
||||||
|
|
||||||
|
|
@ -48,16 +48,16 @@ proc thread5(x: sink MyObjRef): MyObjRef =
|
||||||
os.sleep(1000)
|
os.sleep(1000)
|
||||||
result = x
|
result = x
|
||||||
|
|
||||||
proc ref_forwarding_test =
|
proc ref_forwarding_test =
|
||||||
var x = new(MyObj)
|
var x = new(MyObj)
|
||||||
x[].p = 2
|
x[].p = 2
|
||||||
var y = spawn thread4(x)
|
var y = spawn thread4(x)
|
||||||
|
|
||||||
proc ref_sink_forwarding_test =
|
proc ref_sink_forwarding_test =
|
||||||
var x = new(MyObj)
|
var x = new(MyObj)
|
||||||
x[].p = 2
|
x[].p = 2
|
||||||
var y = spawn thread5(x)
|
var y = spawn thread5(x)
|
||||||
|
|
||||||
ref_forwarding_test()
|
ref_forwarding_test()
|
||||||
ref_sink_forwarding_test()
|
ref_sink_forwarding_test()
|
||||||
sync()
|
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 """
|
discard """
|
||||||
output: '''
|
output: '''
|
||||||
hasPendingOperations: false
|
hasPendingOperations: false
|
||||||
triggerCount: 100
|
triggerCount: 100
|
||||||
'''
|
'''
|
||||||
|
disabled: "windows"
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import asyncDispatch
|
import asyncDispatch
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ discard """
|
||||||
output: '''copied
|
output: '''copied
|
||||||
copied
|
copied
|
||||||
2
|
2
|
||||||
|
destroyed
|
||||||
|
destroyed
|
||||||
copied
|
copied
|
||||||
copied
|
copied
|
||||||
2
|
2
|
||||||
|
|
@ -37,9 +39,9 @@ proc test(a: range[0..1], arg: ObjWithDestructor) =
|
||||||
inc iteration
|
inc iteration
|
||||||
|
|
||||||
case a
|
case a
|
||||||
of 0:
|
of 0:
|
||||||
assert false
|
assert false
|
||||||
of 1:
|
of 1:
|
||||||
echo b
|
echo b
|
||||||
if iteration == 2:
|
if iteration == 2:
|
||||||
break
|
break
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,27 @@
|
||||||
discard """
|
discard """
|
||||||
output: '''----
|
output: '''----1
|
||||||
myobj constructed
|
myobj constructed
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
----
|
----2
|
||||||
mygeneric1 constructed
|
mygeneric1 constructed
|
||||||
mygeneric1 destroyed
|
mygeneric1 destroyed
|
||||||
----
|
----3
|
||||||
mygeneric2 constructed
|
mygeneric2 constructed
|
||||||
mygeneric2 destroyed
|
mygeneric2 destroyed
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
----
|
----4
|
||||||
mygeneric3 constructed
|
mygeneric3 constructed
|
||||||
mygeneric1 destroyed
|
mygeneric1 destroyed
|
||||||
----
|
----5
|
||||||
mydistinctObj constructed
|
mydistinctObj constructed
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
mygeneric2 destroyed
|
mygeneric2 destroyed
|
||||||
------------------
|
------------------8
|
||||||
----
|
|
||||||
----
|
|
||||||
myobj destroyed
|
|
||||||
mygeneric1 destroyed
|
mygeneric1 destroyed
|
||||||
---
|
----6
|
||||||
|
myobj destroyed
|
||||||
|
----7
|
||||||
|
---9
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
myobj destroyed
|
myobj destroyed
|
||||||
'''
|
'''
|
||||||
|
|
@ -114,19 +114,19 @@ proc mydistinctObj =
|
||||||
|
|
||||||
echo "mydistinctObj constructed"
|
echo "mydistinctObj constructed"
|
||||||
|
|
||||||
echo "----"
|
echo "----1"
|
||||||
myobj()
|
myobj()
|
||||||
|
|
||||||
echo "----"
|
echo "----2"
|
||||||
mygeneric1()
|
mygeneric1()
|
||||||
|
|
||||||
echo "----"
|
echo "----3"
|
||||||
mygeneric2[int](10)
|
mygeneric2[int](10)
|
||||||
|
|
||||||
echo "----"
|
echo "----4"
|
||||||
mygeneric3()
|
mygeneric3()
|
||||||
|
|
||||||
echo "----"
|
echo "----5"
|
||||||
mydistinctObj()
|
mydistinctObj()
|
||||||
|
|
||||||
proc caseobj =
|
proc caseobj =
|
||||||
|
|
@ -134,16 +134,16 @@ proc caseobj =
|
||||||
var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
|
var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10))
|
||||||
|
|
||||||
block:
|
block:
|
||||||
echo "----"
|
echo "----6"
|
||||||
var o2 = TCaseObj(kind: B, y: open())
|
var o2 = TCaseObj(kind: B, y: open())
|
||||||
|
|
||||||
block:
|
block:
|
||||||
echo "----"
|
echo "----7"
|
||||||
var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
|
var o3 = TCaseObj(kind: D, innerKind: B, r: "test",
|
||||||
p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
|
p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test"))
|
||||||
|
|
||||||
|
|
||||||
echo "------------------"
|
echo "------------------8"
|
||||||
caseobj()
|
caseobj()
|
||||||
|
|
||||||
proc caseobj_test_sink: TCaseObj =
|
proc caseobj_test_sink: TCaseObj =
|
||||||
|
|
@ -153,7 +153,7 @@ proc caseobj_test_sink: TCaseObj =
|
||||||
result = TCaseObj(kind: B, y: open())
|
result = TCaseObj(kind: B, y: open())
|
||||||
|
|
||||||
|
|
||||||
echo "---"
|
echo "---9"
|
||||||
discard caseobj_test_sink()
|
discard caseobj_test_sink()
|
||||||
|
|
||||||
# issue #14315
|
# issue #14315
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue