From 7e1d0a73daade00173597183a595ded8a373abc4 Mon Sep 17 00:00:00 2001 From: fox0430 Date: Mon, 20 Apr 2026 16:51:07 +0900 Subject: [PATCH] Fix asyncSpawn raiseAssert risk in scheduleDispatch error path --- async_postgres/pg_pool.nim | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/async_postgres/pg_pool.nim b/async_postgres/pg_pool.nim index a9613a4..c5acaec 100644 --- a/async_postgres/pg_pool.nim +++ b/async_postgres/pg_pool.nim @@ -490,6 +490,20 @@ proc failPendingOp(op: PendingPoolOp, e: ref CatchableError) = if not op.queryFut.finished: op.queryFut.fail(e) +proc failAllPending(pool: PgPool, e: ref CatchableError) {.raises: [].} = + ## Fail every queued op with `e`. Marked `raises: []` so the compiler + ## proves the loop cannot leak into an `asyncSpawn`ed caller — any future + ## change to `failPendingOp` or the underlying `Future.fail` that could + ## raise will be caught here at compile time. `Exception` is used (not + ## `CatchableError`) because asyncdispatch's `Future.fail` has inferred + ## effect `Exception` via its callback chain. + try: + while pool.pendingOps.len > 0: + let op = pool.pendingOps.popFirst() + failPendingOp(op, e) + except Exception: + discard + proc executeBatch( pool: PgPool, conn: PgConnection, batch: seq[PendingPoolOp] ): Future[void] {.async.} = @@ -611,9 +625,7 @@ proc scheduleDispatch(pool: PgPool) {.gcsafe, raises: [].} = await pool.dispatchBatchImpl() except CatchableError as e: # Fail any ops still in the queue so their futures don't hang forever. - while pool.pendingOps.len > 0: - let op = pool.pendingOps.popFirst() - failPendingOp(op, e) + pool.failAllPending(e) # Re-schedule if there are remaining ops if pool.pendingOps.len > 0: pool.scheduleDispatch() @@ -625,12 +637,7 @@ proc scheduleDispatch(pool: PgPool) {.gcsafe, raises: [].} = # asyncSpawn should not raise in practice, but the compiler cannot # prove it. Fail any pending ops so their futures do not hang. let err = newException(PgError, "Pipeline dispatch failed: " & e.msg) - try: - while p.pendingOps.len > 0: - let op = p.pendingOps.popFirst() - failPendingOp(op, err) - except Exception: - discard + p.failAllPending(err) p.dispatchScheduled = false try: