Skip to content

feat: Avoid spawning async-io thread if possible#249

Closed
notgull wants to merge 1 commit intomasterfrom
notgull/no-aio-thread
Closed

feat: Avoid spawning async-io thread if possible#249
notgull wants to merge 1 commit intomasterfrom
notgull/no-aio-thread

Conversation

@notgull
Copy link
Copy Markdown
Contributor

@notgull notgull commented Feb 15, 2026

This commit adds conditions that prevent the async-io thread from being
spawned in certain cases. This is important because sometimes async-io
is used in environments where spawning additional threads can interfere
with other functionality in the program; for example, in DPDK poll
loops.

If there is only one async-io::block_on invocation, there is no need to
spawn the "async-io" thread; we can just modify block_on to not give up
the reactor if it can detect that it is the only block_on call running
in the program. From there, we also need to avoid spawning the async-io
thread if there are no resources left when block_on exits. This should
allow async-io to not spawn additional threads in single-threaded
programs where it is not needed.

Currently draft while I work out the kinks.

Closes: #40

@notgull notgull force-pushed the notgull/no-aio-thread branch from 583c2b2 to 65b0e9b Compare February 15, 2026 22:29
@notgull notgull force-pushed the notgull/no-aio-thread branch from 65b0e9b to 4be93d8 Compare March 6, 2026 03:59
@notgull notgull marked this pull request as ready for review March 6, 2026 18:28
@ids1024
Copy link
Copy Markdown

ids1024 commented Mar 10, 2026

Not a blocker for this, but I guess there should similarly be a non-blocking way to drive the reactor without an async-io thread?

async_io::os::unix::reactor_fd() exists, but in addition to still spawning the thread, I'm not sure how it can actually be used (since Reactor::get() and the like aren't public, what is there to do if the reactor polls ready?)

It would be nice to be able to use async-io with the calloop async executor without spawning an extra thread.

@notgull
Copy link
Copy Markdown
Contributor Author

notgull commented Mar 11, 2026

Unfortunately the only way to consistently poll I/O events across platforms is through a blocking strategy. You can nest epoll and kqueue on certain platforms, but this is a no-go everywhere else.

@notgull notgull force-pushed the notgull/no-aio-thread branch 2 times, most recently from 1df67c8 to 23a2f30 Compare March 16, 2026 02:46
@notgull notgull requested review from fogti and zeenix March 16, 2026 02:50
This commit adds conditions that prevent the async-io thread from being
spawned in certain cases. This is important because sometimes async-io
is used in environments where spawning additional threads can interfere
with other functionality in the program; for example, in DPDK poll
loops.

If there is only one async-io::block_on invocation, there is no need to
spawn the "async-io" thread; we can just modify block_on to not give up
the reactor if it can detect that it is the only block_on call running
in the program. From there, we also need to avoid spawning the async-io
thread if there are no resources left when block_on exits. This should
allow async-io to not spawn additional threads in single-threaded
programs where it is not needed.

Signed-off-by: John Nunley <dev@notgull.net>
@notgull notgull force-pushed the notgull/no-aio-thread branch from 23a2f30 to dd56200 Compare March 16, 2026 02:55
async-task = "4.7.1"
blocking = "1"
criterion = { version = "0.7", default-features = false, features = ["cargo_bench_support"] }
fastrand = "2.3.0"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fastrand = "2.3.0"
fastrand = "2.3"

Copy link
Copy Markdown
Member

@fogti fogti left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks quite good overall (I couldn't find any obvious mistakes), but I'm not familiar enough to vouch for its complete correctness.

@zeenix
Copy link
Copy Markdown
Member

zeenix commented Mar 17, 2026

Unfortunately the only way to consistently poll I/O events across platforms is through a blocking strategy. You can nest epoll and kqueue on certain platforms, but this is a no-go everywhere else.

Would it be possible to only support this for such platforms? 🤔 Even if we could only support Linux and one of the BSDs, that would be awesome.

}

#[cold]
fn init() {
Copy link
Copy Markdown
Member

@zeenix zeenix Mar 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defining this inside another function, unnecessarily forces indentation, which is always bad for readability IMO.

BLOCK_ON_COUNT.fetch_sub(1, Ordering::SeqCst);
unparker().unpark();

// Only wake the `async-io` thread iff there are live resources.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: iff.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"iff" is also commonly used to mean "if and only if", but I think then the "only" should be right next to the "iff".

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've never heard that before in my life so I'm not sure that it's common enough. 😂 Best to be clear IMHO.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, it might be more common among mathematicians.

Comment on lines +18 to +21
static ALIVE_RESOURCE_COUNT: AtomicUsize = AtomicUsize::new(0);

/// Raw unparker for the "async-io" thread.
static UNPARKER: OnceLock<parking::Unparker> = OnceLock::new();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moving to 2 separate globals that are tightly-coupled and then to be handled in a thread-safe manner, and being accessed directly from multiple places, seems a bit dangerous to me. I am too tired right now to suggest something better though 😭 but I'll think about it..

@notgull notgull closed this Apr 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

Avoid spawning the async-io thread if possible

4 participants