This commit is contained in:
araq
2026-02-09 17:17:26 +01:00
parent 3ed9a5cc81
commit 76c17a2194
2 changed files with 51 additions and 37 deletions

View File

@@ -848,19 +848,11 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) =
let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x)
xenv.typ = getSysType(c.g, c.info, tyPointer)
# YRC uses dedicated runtime procs for the write barrier on the env pointer:
# Closure envs are always polymorphic, so pass nil for desc (uses Dyn variant):
if c.g.config.selectedGC == gcYrc:
let nilDesc = newNodeIT(nkNilLit, c.info, getSysType(c.g, c.info, tyPointer))
case c.kind
of attachedAsgn, attachedDup:
body.add callCodegenProc(c.g, "nimAsgnYrc", c.info, genAddr(c, xenv), y, nilDesc)
return
of attachedSink:
body.add callCodegenProc(c.g, "nimSinkYrc", c.info, genAddr(c, xenv), y, nilDesc)
return
else: discard # fall through for destructor, trace, wasMoved
# Closures are (fnPtr, env) pairs -- nimAsgnYrc/nimSinkYrc operate on single
# pointers so they cannot handle a closure struct. Instead we fall through to
# the regular ORC cyclic path which copies the whole struct via newAsgnStmt
# and calls nimIncRefCyclic / nimDecRefIsLastCyclicDyn on the env pointer.
# Those procs are already YRC-aware (they buffer into striped queues).
let isCyclic = c.g.config.selectedGC in {gcOrc, gcYrc}
let tmp =
if isCyclic and c.kind in {attachedAsgn, attachedSink, attachedDup}:

View File

@@ -122,7 +122,8 @@ include threadids
type
Stripe = object
lockInc: Lock
when not defined(yrcAtomics):
lockInc: Lock
toIncLen: int
toInc: array[QueueSize, Cell]
lockDec: Lock
@@ -143,33 +144,53 @@ proc nimIncRefCyclic(p: pointer; cyclic: bool) {.compilerRtl, inl.} =
let h = head(p)
when optimizedOrc:
if cyclic: h.rc = h.rc or maybeCycle
let idx = getStripeIdx()
while true:
var overflow = false
withLock stripes[idx].lockInc:
if stripes[idx].toIncLen < QueueSize:
stripes[idx].toInc[stripes[idx].toIncLen] = h
stripes[idx].toIncLen += 1
else:
overflow = true
if overflow:
withLock gYrcGlobalLock:
for i in 0..<NumStripes:
withLock stripes[i].lockInc:
for j in 0..<stripes[i].toIncLen:
let x = stripes[i].toInc[j]
x.rc = x.rc +% rcIncrement
stripes[i].toIncLen = 0
when defined(yrcAtomics):
let s = getStripeIdx()
let slot = atomicFetchAdd(addr stripes[s].toIncLen, 1, ATOMIC_ACQ_REL)
if slot < QueueSize:
atomicStoreN(addr stripes[s].toInc[slot], h, ATOMIC_RELEASE)
else:
break
withLock gYrcGlobalLock:
h.rc = h.rc +% rcIncrement
for i in 0..<NumStripes:
let len = atomicExchangeN(addr stripes[i].toIncLen, 0, ATOMIC_ACQUIRE)
for j in 0..<min(len, QueueSize):
let x = atomicLoadN(addr stripes[i].toInc[j], ATOMIC_ACQUIRE)
x.rc = x.rc +% rcIncrement
else:
let idx = getStripeIdx()
while true:
var overflow = false
withLock stripes[idx].lockInc:
if stripes[idx].toIncLen < QueueSize:
stripes[idx].toInc[stripes[idx].toIncLen] = h
stripes[idx].toIncLen += 1
else:
overflow = true
if overflow:
withLock gYrcGlobalLock:
for i in 0..<NumStripes:
withLock stripes[i].lockInc:
for j in 0..<stripes[i].toIncLen:
let x = stripes[i].toInc[j]
x.rc = x.rc +% rcIncrement
stripes[i].toIncLen = 0
else:
break
proc mergePendingRoots() =
for i in 0..<NumStripes:
withLock stripes[i].lockInc:
for j in 0..<stripes[i].toIncLen:
let x = stripes[i].toInc[j]
when defined(yrcAtomics):
let incLen = atomicExchangeN(addr stripes[i].toIncLen, 0, ATOMIC_ACQUIRE)
for j in 0..<min(incLen, QueueSize):
let x = atomicLoadN(addr stripes[i].toInc[j], ATOMIC_ACQUIRE)
x.rc = x.rc +% rcIncrement
stripes[i].toIncLen = 0
else:
withLock stripes[i].lockInc:
for j in 0..<stripes[i].toIncLen:
let x = stripes[i].toInc[j]
x.rc = x.rc +% rcIncrement
stripes[i].toIncLen = 0
withLock stripes[i].lockDec:
for j in 0..<stripes[i].toDecLen:
let (c, desc) = stripes[i].toDec[j]
@@ -483,7 +504,8 @@ proc nimMarkCyclic(p: pointer) {.compilerRtl, inl.} =
# Initialize locks at module load
initLock(gYrcGlobalLock)
for i in 0..<NumStripes:
initLock(stripes[i].lockInc)
when not defined(yrcAtomics):
initLock(stripes[i].lockInc)
initLock(stripes[i].lockDec)
{.pop.}