Ensure channels don't leak exception effects (#25318)

The forward declarations cause `Exception` to be inferred - also,
`llrecv` is an internal implementation detail and the type of the
received item is controlled by generics, thus the ValueError raised
there seems out of place for the generic api.
This commit is contained in:
Jacek Sieka
2025-12-01 22:59:26 +01:00
committed by GitHub
parent a773178e2b
commit 91febf1f4c
13 changed files with 64 additions and 45 deletions

View File

@@ -118,21 +118,21 @@ when not defined(js):
template `rawType=`(x: var Any, p: PNimType) =
x.rawTypePtr = cast[pointer](p)
proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc, raises: [].}
when not defined(gcDestructors):
proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.}
proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.}
proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.}
proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.}
proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.}
proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc, raises: [].}
proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc, raises: [].}
proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc, raises: [].}
proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc, raises: [].}
proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc, raises: [].}
else:
proc nimNewObj(size, align: int): pointer {.importCompilerProc.}
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.}
proc nimNewObj(size, align: int): pointer {.importCompilerProc, raises: [].}
proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc, raises: [].}
proc prepareSeqAddUninit(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {.
importCompilerProc.}
importCompilerProc, raises: [].}
proc zeroNewElements(len: int; p: pointer; addlen, elemSize, elemAlign: int) {.
importCompilerProc.}
importCompilerProc, raises: [].}
include system/ptrarith

View File

@@ -138,6 +138,8 @@
## localChannelExample() # "Hello from the main thread!"
## ```
{.push raises: [], gcsafe.}
when not declared(ThisIsSystem):
{.error: "You must not import this module explicitly".}
@@ -390,7 +392,7 @@ proc llRecv(q: PRawChannel, res: pointer, typ: PNimType) =
q.ready = false
if typ != q.elemType:
releaseSys(q.lock)
raise newException(ValueError, "cannot receive message of wrong type")
raiseAssert "cannot receive message of wrong type"
rawRecv(q, res, typ)
if q.maxItems > 0 and q.count == q.maxItems - 1:
# Parent thread is awaiting in send. Wake it up.
@@ -455,3 +457,5 @@ proc ready*[TMsg](c: var Channel[TMsg]): bool =
## new messages.
var q = cast[PRawChannel](addr(c))
result = q.ready
{.pop.}

View File

@@ -97,7 +97,7 @@ type
waZctDecRef, waPush
#, waDebug
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].}
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [], gcsafe.}
# A ref type can have a finalizer that is called before the object's
# storage is freed.
@@ -481,17 +481,17 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer =
{.pop.} # .stackTrace off
{.pop.} # .profiler off
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
result = rawNewObj(typ, size, gch)
when defined(memProfiler): nimProfile(size)
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline, raises: [].} =
result = rawNewObj(typ, size, gch)
zeroMem(result, size)
when defined(memProfiler): nimProfile(size)
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
# `newObj` already uses locks, so no need for them here.
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObj(typ, size)
@@ -500,7 +500,7 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
when defined(memProfiler): nimProfile(size)
{.pop.}
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline, raises: [].} =
# generates a new object and sets its reference counter to 1
incTypeSize typ, size
sysAssert(allocInv(gch.region), "newObjRC1 begin")
@@ -528,7 +528,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} =
when defined(memProfiler): nimProfile(size)
{.push overflowChecks: on.}
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObjRC1(typ, size)
cast[PGenericSeq](result).len = len
@@ -670,7 +670,7 @@ proc doOperation(p: pointer, op: WalkOp) =
add(gch.tempStack, c)
#of waDebug: debugGraph(c)
proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} =
proc nimGCvisit(d: pointer, op: int) {.compilerRtl, raises: [].} =
doOperation(d, WalkOp(op))
proc collectZCT(gch: var GcHeap): bool {.benign, raises: [].}

View File

@@ -46,8 +46,8 @@ var
newObjHook*: proc (typ: PNimType, size: int): pointer {.nimcall, tags: [], raises: [], gcsafe.}
traverseObjHook*: proc (p: pointer, op: int) {.nimcall, tags: [], raises: [], gcsafe.}
proc nimGCvisit(p: pointer, op: int) {.inl, compilerRtl.} =
proc nimGCvisit(p: pointer, op: int) {.inl, compilerRtl, raises: [].} =
traverseObjHook(p, op)
proc newObj(typ: PNimType, size: int): pointer {.inl, compilerRtl.} =
proc newObj(typ: PNimType, size: int): pointer {.inl, compilerRtl, raises: [].} =
result = newObjHook(typ, size)

View File

@@ -36,7 +36,7 @@ type
# local
waMarkPrecise # fast precise marking
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].}
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [], gcsafe.}
# A ref type can have a finalizer that is called before the object's
# storage is freed.
@@ -289,23 +289,23 @@ when useCellIds:
{.pop.}
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
result = rawNewObj(typ, size, gch)
zeroMem(result, size)
when defined(memProfiler): nimProfile(size)
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
result = rawNewObj(typ, size, gch)
when defined(memProfiler): nimProfile(size)
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
result = rawNewObj(typ, size, gch)
zeroMem(result, size)
when defined(memProfiler): nimProfile(size)
when not defined(nimSeqsV2):
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
# `newObj` already uses locks, so no need for them here.
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObj(typ, size)
@@ -313,7 +313,7 @@ when not defined(nimSeqsV2):
cast[PGenericSeq](result).reserved = len
when defined(memProfiler): nimProfile(size)
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
let size = align(GenericSeqSize, typ.base.align) + len * typ.base.size
result = newObj(typ, size)
cast[PGenericSeq](result).len = len
@@ -346,7 +346,7 @@ when not defined(nimSeqsV2):
result = cellToUsr(res)
when defined(memProfiler): nimProfile(newsize-oldsize)
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
proc growObj(old: pointer, newsize: int): pointer {.rtl, raises: [].} =
result = growObj(old, newsize, gch)
{.push profiler:off.}

View File

@@ -6,6 +6,8 @@
# distribution, for details about the copyright.
#
{.push raises: [], gcsafe.}
# "Stack GC" for embedded devices or ultra performance requirements.
import std/private/syslocks
@@ -39,7 +41,7 @@ else:
# We also support 'finalizers'.
type
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.}
Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [], gcsafe.}
# A ref type can have a finalizer that is called before the object's
# storage is freed.
@@ -305,26 +307,26 @@ proc rawNewSeq(r: var MemRegion, typ: PNimType, size: int): pointer =
res.region = addr(r)
result = res +! sizeof(SeqHeader)
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
sysAssert typ.kind notin {tySequence, tyString}, "newObj cannot be used to construct seqs"
result = rawNewObj(tlRegion, typ, size)
zeroMem(result, size)
when defined(memProfiler): nimProfile(size)
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
sysAssert typ.kind notin {tySequence, tyString}, "newObj cannot be used to construct seqs"
result = rawNewObj(tlRegion, typ, size)
when defined(memProfiler): nimProfile(size)
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
let size = roundup(align(GenericSeqSize, typ.base.align) + len * typ.base.size, MemAlign)
result = rawNewSeq(tlRegion, typ, size)
zeroMem(result, size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl, raises: [].} =
let size = roundup(len + GenericSeqSize, MemAlign)
result = rawNewSeq(tlRegion, typ, size)
if init: zeroMem(result, size)
@@ -332,14 +334,14 @@ proc newStr(typ: PNimType, len: int; init: bool): pointer {.compilerRtl.} =
cast[PGenericSeq](result).reserved = len
{.pop.}
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} =
proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, raises: [].} =
result = rawNewObj(tlRegion, typ, size)
zeroMem(result, size)
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl.} =
proc newSeqRC1(typ: PNimType, len: int): pointer {.compilerRtl, raises: [].} =
result = newSeq(typ, len)
proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer {.raises: [].} =
let sh = cast[ptr SeqHeader](old -! sizeof(SeqHeader))
let typ = sh.typ
result = rawNewSeq(sh.region[], typ,
@@ -351,7 +353,7 @@ proc growObj(regionUnused: var MemRegion; old: pointer, newsize: int): pointer =
copyMem(result, old, oldsize)
dealloc(sh.region[], old, roundup(oldsize, MemAlign))
proc growObj(old: pointer, newsize: int): pointer {.rtl.} =
proc growObj(old: pointer, newsize: int): pointer {.rtl, raises: [].} =
result = growObj(tlRegion, old, newsize)
proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} =
@@ -434,3 +436,5 @@ proc nimGC_setStackBottom(theStackBottom: pointer) = discard
proc nimGCref(x: pointer) {.compilerproc.} = discard
proc nimGCunref(x: pointer) {.compilerproc.} = discard
{.pop.}

View File

@@ -1,4 +1,4 @@
{.push raises: [], gcsafe.}
proc boehmGCinit {.importc: "GC_init", boehmGC.}
@@ -95,7 +95,7 @@ proc initGC() =
when hasThreadSupport:
boehmGC_allow_register_threads()
proc boehmgc_finalizer(obj: pointer, typedFinalizer: (proc(x: pointer) {.cdecl.})) =
proc boehmgc_finalizer(obj: pointer, typedFinalizer: (proc(x: pointer) {.cdecl, raises: [], gcsafe.})) =
typedFinalizer(obj)
@@ -138,3 +138,5 @@ proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard
include "system/cellsets"
{.pop.}

View File

@@ -1,3 +1,4 @@
{.push raises: [], gcsafe.}
when defined(windows):
const goLib = "libgo.dll"
@@ -151,3 +152,5 @@ proc alloc0(r: var MemRegion, size: int): pointer =
proc dealloc(r: var MemRegion, p: pointer) = dealloc(p)
proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard
{.pop.}

View File

@@ -20,7 +20,7 @@ proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size)
{.push overflowChecks: on.}
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc, raises: [].} =
result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len

View File

@@ -60,10 +60,10 @@ elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc):
when defined(nogc):
proc GC_getStatistics(): string = ""
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
proc newObj(typ: PNimType, size: int): pointer {.compilerproc, raises: [].} =
result = alloc0(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc, raises: [].} =
result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size)
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len

View File

@@ -7,6 +7,8 @@
# distribution, for details about the copyright.
#
{.push raises: [], gcsafe.}
proc roundup(x, v: int): int {.inline.} =
result = (x + (v-1)) and not (v-1)
sysAssert(result >= x, "roundup: result < x")
@@ -216,3 +218,5 @@ elif hostOS == "standalone" or defined(StandaloneHeapSize):
else:
{.error: "Port memory manager to your platform".}
{.pop.}

View File

@@ -15,6 +15,7 @@
# we don't use refcounts because that's a behaviour
# the programmer may not want
{.push raises: [], gcsafe.}
proc dataPointer(a: PGenericSeq, elemAlign: int): pointer =
cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign))
@@ -103,7 +104,7 @@ proc toNimStr(str: cstring, len: int): NimString {.compilerproc.} =
copyMem(addr(result.data), str, len)
result.data[len] = '\0'
proc toOwnedCopy(src: NimString): NimString {.inline.} =
proc toOwnedCopy(src: NimString): NimString {.inline, raises: [].} =
## Expects `src` to be not nil and initialized (len and terminating zero set)
result = rawNewStringNoInit(src.len)
result.len = src.len
@@ -149,7 +150,7 @@ proc copyStringRC1(src: NimString): NimString {.compilerRtl.} =
if (src.reserved and strlitFlag) != 0:
result.reserved = (result.reserved and not strlitFlag) or seqShallowFlag
proc copyDeepString(src: NimString): NimString {.inline.} =
proc copyDeepString(src: NimString): NimString {.inline, raises: [].} =
if src != nil:
result = toOwnedCopy(src)
@@ -358,3 +359,4 @@ func capacity*[T](self: seq[T]): int {.inline.} =
let sek = cast[PGenericSeq](self)
result = if sek != nil: sek.space else: 0
{.pop.}

View File

@@ -12,14 +12,14 @@ var
chan1.open()
chan2.open()
proc routeMessage*(msg: BackendMessage) =
proc routeMessage*(msg: BackendMessage) {.raises: [], gcsafe.} = # no exceptions!
discard chan2.trySend(msg)
var
recv: Thread[void]
stopToken: Atomic[bool]
proc recvMsg() =
proc recvMsg() {.raises: [], gcsafe.} = # no exceptions!
while not stopToken.load(moRelaxed):
let resp = chan1.tryRecv()
if resp.dataAvailable: