Summary
When synchronous promises are deeply nested, the call chain can exhaust the ThreadPoolExecutor (currently max_workers=128 in Defaults.PROMISING_THREAD_POOL). The deepest synchronous function waits for a child promise that can never be scheduled because all worker threads are already occupied by the waiting chain — resulting in a deadlock.
Proposed solution
Track synchronous call depth along the code path that schedules work into the ThreadPoolExecutor and raises a clear, actionable error (e.g., ThreadPoolDepthExceededError or similar) when depth >= executor.max_workers, instead of deadlocking silently.
The check should be disableable via a toggle (e.g., a flag on Defaults, an env-var like PROMISING_DISABLE_DEPTH_CHECK, or an optional parameter on Promise/PromisingFunction) so callers who know their workload is safe can opt out.
Acceptance criteria
References
/cc @teremterem
Summary
When synchronous promises are deeply nested, the call chain can exhaust the
ThreadPoolExecutor(currentlymax_workers=128inDefaults.PROMISING_THREAD_POOL). The deepest synchronous function waits for a child promise that can never be scheduled because all worker threads are already occupied by the waiting chain — resulting in a deadlock.Proposed solution
Track synchronous call depth along the code path that schedules work into the
ThreadPoolExecutorand raises a clear, actionable error (e.g.,ThreadPoolDepthExceededErroror similar) when depth >=executor.max_workers, instead of deadlocking silently.The check should be disableable via a toggle (e.g., a flag on
Defaults, an env-var likePROMISING_DISABLE_DEPTH_CHECK, or an optional parameter onPromise/PromisingFunction) so callers who know their workload is safe can opt out.Acceptance criteria
max_workers.References
PromisingContext::get_trace()#79PromisingContext::get_trace()#79 (comment)/cc @teremterem