Skip to content

linux: Feature gate async runtime, allowing opting between Tokio or smol#27

Merged
mxinden merged 10 commits into
libp2p:masterfrom
jxs:feature-gate-runtime
Nov 4, 2022
Merged

linux: Feature gate async runtime, allowing opting between Tokio or smol#27
mxinden merged 10 commits into
libp2p:masterfrom
jxs:feature-gate-runtime

Conversation

@jxs

@jxs jxs commented Sep 28, 2022

Copy link
Copy Markdown
Member

Following #21 and confirming @dignifiedquire's #21 (comment), we still can't define features per target.

But it seems we can define features for dependencies, and then dependencies can be defined per target.
With that we can apply the similar technique rtnetlink uses to support both runtimes, with the difference that in our case as smol_socket is the default (to maintain backwards compatibility) we invert the feature combination and if both are enabled tokio takes priority.

Only downside that comes to mind with this approach is if someone uses if-watch with default-features = false crate compilation breaks (which is also the reason this change is technically a breaking change). We can use compile_error! to give a better compilation error.

CC @thomaseizinger

@dignifiedquire

Copy link
Copy Markdown
Member

nice solution 👍

Comment thread src/linux.rs Outdated
Comment thread Cargo.toml Outdated
Comment on lines +13 to +14
tokio = ["rtnetlink/tokio_socket"]
smol_socket = ["rtnetlink/smol_socket"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

So what happens if I am on windows and activate the tokio feature flag?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So, I tried compiling with default features, tokio and --no-default-features.
All compile normally and produce the same Cargo.lock.
Btw should we name the features as rtnetlink names them or generify to smol and tokio ?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

That is surprising to me. I would have expected cargo to choke on a feature that points to a dependency that is not present in the manifest for this target.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd prefer if we just name them after the executor and drop the _socket.

by making smol the default `IfWatcher` and introducing `TokioIfWatcher`.

@thomaseizinger thomaseizinger left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks! Couple of comments to ponder over :)

Is my understanding correct that rtnetlinks handling of sockets with this try_spawn_blocking stuff doesn't actually affect us because we are not using those APIs?

That is great news because I think it means we can actually fully encapsulate the way we enable executors without any upstream changes!

Comment thread src/linux.rs Outdated
Comment thread Cargo.toml Outdated
Comment on lines +13 to +14
tokio = ["rtnetlink/tokio_socket"]
smol_socket = ["rtnetlink/smol_socket"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I'd prefer if we just name them after the executor and drop the _socket.

Comment thread src/lib.rs Outdated
Comment thread CHANGELOG.md Outdated
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.1.0] - [unreleased]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Missing version bump in Cargo.toml and it is also breaking atm because you are removing the IfWatcher symbol for linux :)

@mxinden mxinden left a comment

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.

Thanks for looking into this @jxs!

Comment thread src/linux.rs
@mxinden

mxinden commented Oct 14, 2022

Copy link
Copy Markdown
Member

@jxs if I am not mistaken this is not blocked on anything, right? Not to rush, just to make sure you are not waiting on anyone's input.

@jxs
jxs force-pushed the feature-gate-runtime branch 2 times, most recently from 3a0c0ab to 7d6efc4 Compare October 17, 2022 19:47
@jxs
jxs force-pushed the feature-gate-runtime branch from 7d6efc4 to 4818613 Compare October 17, 2022 19:52
Comment thread Cargo.toml Outdated
Comment thread examples/if_watch.rs
Comment thread src/apple.rs Outdated
pub fn new() -> Result<Self> {
let (tx, rx) = mpsc::channel(1);
std::thread::spawn(|| background_task(tx));
T::spawn(async { background_task(tx) });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Spawning a non-async function is a bit odd. Can we make it async?

I think this will block the executor thread otherwise.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I think this will block the executor thread otherwise.

interesting, why do you say this?

btw was able to test on a mac and confirm background_task's CFRunLoop::run_current() seems to run forever, and per tokio spawn_blocking doc for example:

This function is intended for non-async operations that eventually finish on their own. If you want to spawn an ordinary thread, you should use thread::spawn instead.

so we should probably maintain the plain old std::thread::spawn, wdyt?

@thomaseizinger thomaseizinger Oct 18, 2022

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think this will block the executor thread otherwise.

interesting, why do you say this?

Unless interrupted otherwise, a task will run on an executor thread until it yields, i.e. returns Poll::Pending.

async_std has a mechanism to detect blocked executor threads and spawn new ones: https://async.rs/blog/stop-worrying-about-blocking-the-new-async-std-runtime/

Tokio doesn't though and it is a conscious design decision AFAIK.

btw was able to test on a mac and confirm background_task's CFRunLoop::run_current() seems to run forever, and per tokio spawn_blocking doc for example:

This function is intended for non-async operations that eventually finish on their own. If you want to spawn an ordinary thread, you should use thread::spawn instead.

But you are not using spawn_blocking right? So how is that relevant?

so we should probably maintain the plain old std::thread::spawn, wdyt?

Can we refactor this to be similar to the linux implementation where we register a waker and call it in a callback?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Unless interrupted otherwise, a task will run on an executor thread until it yields, i.e. returns Poll::Pending.

async_std has a mechanism to detect blocked executor threads and spawn new ones: https://async.rs/blog/stop-worrying->about-blocking-the-new-async-std-runtime/

Tokio doesn't though and it is a conscious design decision AFAIK.

Right yeah, but a function just by not being async doesn't mean it will block the thread.

But you are not using spawn_blocking right? So how is that relevant?

because background_task's CFRunLoop::run_current() seems to run forever, therefore blocking the thread.

Can we refactor this to be similar to the linux implementation where we register a waker and call it in a callback?

Yeah I think so, but there still still need for a blocking thread as CFRunLoop::run_current() is blocking, while the Windows NotifyIpInterfaceChange isn't.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Unless interrupted otherwise, a task will run on an executor thread until it yields, i.e. returns Poll::Pending.
async_std has a mechanism to detect blocked executor threads and spawn new ones: https://async.rs/blog/stop-worrying->about-blocking-the-new-async-std-runtime/
Tokio doesn't though and it is a conscious design decision AFAIK.

Right yeah, but a function just by not being async doesn't mean it will block the thread.

That is correct. I think we are saying the same thing, it is CFRunLoop::run_current() that is the problem.

But you are not using spawn_blocking right? So how is that relevant?

because background_task's CFRunLoop::run_current() seems to run forever, therefore blocking the thread.

Can we refactor this to be similar to the linux implementation where we register a waker and call it in a callback?

Yeah I think so, but there still still need for a blocking thread as CFRunLoop::run_current() is blocking, while the Windows NotifyIpInterfaceChange isn't.

That really is unfortunate. I guess we will have to stick with a regular thread then for MacOS.

@jxs jxs Oct 22, 2022

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

That really is unfortunate. I guess we will have to stick with a regular thread then for MacOS.

yeah 😞 Reverted to an implementation similar to Windows ptal Thomas. Still, I think this approach makes sense and is more idiomatic the inicial one of overriding features

@jxs
jxs force-pushed the feature-gate-runtime branch from 2efad4b to ba8e1d4 Compare October 18, 2022 15:39
jxs added 2 commits October 20, 2022 16:56
to fix android ci build. It was failing with: `libif_watch.so` doesn't exist.

@thomaseizinger thomaseizinger left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks, LGTM!

@mxinden mxinden left a comment

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.

Thanks for the thorough work on this.

Comment thread CHANGELOG.md Outdated
@mxinden
mxinden merged commit 31c4780 into libp2p:master Nov 4, 2022
@mxinden

mxinden commented Nov 4, 2022

Copy link
Copy Markdown
Member

Tagged and published.

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.

5 participants