Skip to content

std.Io: guarantee when async() returns, task is already completed or has been successfully assigned a unit of concurrency#25998

Merged
andrewrk merged 10 commits intomasterfrom
std.Io.Threaded-async-guarantee
Nov 22, 2025
Merged

std.Io: guarantee when async() returns, task is already completed or has been successfully assigned a unit of concurrency#25998
andrewrk merged 10 commits intomasterfrom
std.Io.Threaded-async-guarantee

Conversation

@andrewrk
Copy link
Member

@andrewrk andrewrk commented Nov 21, 2025

This is @kristoff-it's io-threaded-no-queue branch, rebased on latest master with some commits on top.

Loris, please review the diff carefully as there were nontrivial conflicts that I rebased on your behalf.

@andrewrk andrewrk requested a review from kristoff-it November 21, 2025 16:53
@kristoff-it
Copy link
Member

Looked at the code, tested it in both multi-threaded and single-threaded mode with my redis client bechmark and also with the recursive ziggy formatter. All looks good to me!

@andrewrk andrewrk enabled auto-merge November 21, 2025 21:00
@andrewrk
Copy link
Member Author

error: 'crypto.kangarootwelve.test.KT128 sequential and parallel produce same output for large inputs' failed: on many targets

kristoff-it and others added 10 commits November 21, 2025 19:54
This is a reimplementation of Io.Threaded that fixes the issues
highlighted in the recent Zulip discussion. It's poorly tested but it
does successfully run to completion the litmust test example that I
offered in the discussion.

This implementation has the following key design decisions:

- `t.cpu_count` is used as the threadpool size.
- `t.concurrency_limit` is used as the maximum number of
  "burst, one-shot" threads that can be spawned by `io.concurrent` past
  `t.cpu_count`.
- `t.available_thread_count` is the number of threads in the pool that
  is not currently busy with work (the bookkeeping happens in the worker
  function).
- `t.one_shot_thread_count` is the number of active threads that were
  spawned by `io.concurrent` past `t.cpu_count`.

In this implementation:

- `io.async` first tries to decrement `t.available_thread_count`. If
  there are no threads available, it tries to spawn a new one if possible,
  otherwise it runs the task immediately.
- `io.concurrent` first tries to use a thread in the pool same as
  `io.async`, but on failure (no available threads and pool size limit
  reached) it tries to spawn a new one-shot thread. One shot threads
  run a different main function that just executes one task, decrements
  the number of active one shot threads, and then exits.

A relevant future improvement is to have one-shot threads stay on for a
few seconds (and potentially pick up a new task) to amortize spawning
costs.
while still preserving the guarantee about async() being assigned a unit
of concurrency (or immediately running the task), this change:
* retains the error from calling getCpuCount()
* spawns all threads in detached mode, using WaitGroup to join them
* treats all workers the same regardless of whether they are processing
  concurrent or async tasks. one thread pool does all the work, while
  respecting async and concurrent limits.
@andrewrk andrewrk force-pushed the std.Io.Threaded-async-guarantee branch from fcc6c2d to 7096e66 Compare November 22, 2025 03:54
@andrewrk andrewrk disabled auto-merge November 22, 2025 04:56
@andrewrk andrewrk merged commit 2ea55d7 into master Nov 22, 2025
1 of 9 checks passed
@andrewrk andrewrk deleted the std.Io.Threaded-async-guarantee branch November 22, 2025 04:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants