Is your feature request related to a problem? Please describe.
#23486 throttles how many items of a concurrently(..) group are driven at once.
The implementation scopes a tokio::sync::Semaphore to each gen_all call, with a window of max(64, local_parallelism * 4, remote_parallelism * 2).
Two things about it are ad hoc, per review discussion on the PR:
- The
4x / 2x multipliers are guesses and untuned for remote execution.
- Per-call scoping means the global bound is not a bound at all so worst-case admitted concurrency is
window^depth.
The per-call scoping exists because a single shared pool deadlocks.
Describe the solution you'd like
A dependency-aware semaphore with permit inheritance. A single session-global pool of permits caps memory globally. An acquisition may borrow the permit held by an ancestor on its requester chain with at most one borrower per permit at a time.
Implementation suggestion:
- A custom primitive (Semaphore has no borrow). Two waker queues. Acquire would first try pool, then try borrowing the nearest ancestor's permit, then wait on both.
- Intra-node nesting (
AllItem::Concurrent): the ancestor's borrow-permit is task-local, threaded through gen_all → gen_all_item → gen_all.
- Cross-node nesting (
AllItem::Call): the borrow check is "does any current dependent chain of this node hold a permit?" The graph crate already tracks dependents so we can expose that at acquire time and re-check when a new dependent attaches while blocked.
- Borrowability derives dynamically from current dependents.
Describe alternatives you've considered
Keep per-call windows and only tune the multipliers. Never fixes the W^depth blow-up on deeply nested concurrently usage. The multipliers as is have no principled setting because the quantity they bound is not global.
Additional context
#23486
Is your feature request related to a problem? Please describe.
#23486 throttles how many items of a
concurrently(..)group are driven at once.The implementation scopes a
tokio::sync::Semaphoreto eachgen_allcall, with a window ofmax(64, local_parallelism * 4, remote_parallelism * 2).Two things about it are ad hoc, per review discussion on the PR:
4x/2xmultipliers are guesses and untuned for remote execution.window^depth.The per-call scoping exists because a single shared pool deadlocks.
Describe the solution you'd like
A dependency-aware semaphore with permit inheritance. A single session-global pool of permits caps memory globally. An acquisition may borrow the permit held by an ancestor on its requester chain with at most one borrower per permit at a time.
Implementation suggestion:
AllItem::Concurrent): the ancestor's borrow-permit is task-local, threaded throughgen_all→gen_all_item→gen_all.AllItem::Call): the borrow check is "does any current dependent chain of this node hold a permit?" Thegraphcrate already tracks dependents so we can expose that at acquire time and re-check when a new dependent attaches while blocked.Describe alternatives you've considered
Keep per-call windows and only tune the multipliers. Never fixes the
W^depthblow-up on deeply nestedconcurrentlyusage. The multipliers as is have no principled setting because the quantity they bound is not global.Additional context
#23486