-
Notifications
You must be signed in to change notification settings - Fork 27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
linux: Feature gate async runtime, allowing opting between Tokio or smol #27
Conversation
nice solution 👍 |
Cargo.toml
Outdated
tokio = ["rtnetlink/tokio_socket"] | ||
smol_socket = ["rtnetlink/smol_socket"] |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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`.
There was a problem hiding this comment.
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 rtnetlink
s 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!
Cargo.toml
Outdated
tokio = ["rtnetlink/tokio_socket"] | ||
smol_socket = ["rtnetlink/smol_socket"] |
There was a problem hiding this comment.
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
.
CHANGELOG.md
Outdated
@@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file. | |||
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] |
There was a problem hiding this comment.
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 :)
There was a problem hiding this comment.
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!
@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. |
3a0c0ab
to
7d6efc4
Compare
7d6efc4
to
4818613
Compare
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) }); |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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
'sCFRunLoop::run_current()
seems to run forever, and pertokio
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
'sCFRunLoop::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 WindowsNotifyIpInterfaceChange
isn't.
That really is unfortunate. I guess we will have to stick with a regular thread then for MacOS.
There was a problem hiding this comment.
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
2efad4b
to
ba8e1d4
Compare
to fix android ci build. It was failing with: `libif_watch.so` doesn't exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, LGTM!
There was a problem hiding this comment.
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.
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 assmol_socket
is the default (to maintain backwards compatibility) we invert the feature combination and if both are enabledtokio
takes priority.Only downside that comes to mind with this approach is if someone uses
if-watch
withdefault-features = false
crate compilation breaks (which is also the reason this change is technically a breaking change). We can usecompile_error!
to give a better compilation error.CC @thomaseizinger