-
Notifications
You must be signed in to change notification settings - Fork 341
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
feat: use smol as runtime #757
Conversation
I'm a bit hesitant on adding Can we hold back on this aspect? I know it is substantial for WASM, but we should have a chat on our ongoing direction first. |
@k-nasa do you have any idea why the no-std build is failing. It seems to be that
|
@dignifiedquire I researched 🔍 Try the following command.
It seems to me that this is the part that caused the build to fail. By commenting out, you can build without adding [target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-timer = "0.2.4"
wasm-bindgen-futures = "0.4.10"
futures-channel = "0.3.4" |
@k-nasa thanks, those dependencies where the issue, managed to fix it |
After chatting with @dignifiedquire, I want to retract my hesitation to add spawn_local. |
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.
This is looking fantastic. Amazing work @dignifiedquire!
I did spot a single regression regarding the task::spawn
log point which ideally could be fixed before merging. And there's a few formatting nits, but those shouldn't be a reason to hold of merging.
Incredibly excited for this!
[target.'cfg(target_arch = "wasm32")'.dependencies] | ||
wasm-timer = { version = "0.2.4", optional = true } | ||
wasm-bindgen-futures = { version = "0.4.10", optional = true } | ||
futures-channel = { version = "0.3.4", optional = true } |
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.
Ideally we could use our own channels in this context, which already work. But for the purpose of this PR this seems fine.
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.
They didn't quite do what I needed, we can improve on that later on.
assert_eq!(&other_buffer, &[5, 6, 7, 8]); | ||
Ok(()) | ||
}) | ||
}); |
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.
Why was ?
removed in this file? Is that a stylistic choice, or does this point to a change in behavior?
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.
because block_on
can't return a value in wasm, making this blow up, now that I cfged that out I can undo that change probably
@@ -230,29 +223,31 @@ cfg_unix! { | |||
|
|||
impl IntoRawFd for TcpListener { | |||
fn into_raw_fd(self) -> RawFd { | |||
self.watcher.into_inner().into_raw_fd() | |||
self.watcher.into_raw_fd() |
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'm excited about the simplifications here.
[target.'cfg(target_arch = "wasm32")'.dev-dependencies] | ||
wasm-bindgen-test = "0.3.10" |
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.
This is so exciting!
// Log this `spawn` operation. | ||
trace!("spawn", { | ||
task_id: task.id().0, | ||
parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0), |
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.
We should keep this functionality. If performance is a concern we can make it a no-op unless enabled:
if log_enabled!(Trace) {
trace!("spawn", {
task_id: wrapped.tag.id().0,
parent_task_id: TaskLocalsWrapper::get_current(|t| t.id().0).unwrap_or(0),
});
}
Though not documented this is meant for public consumption. If it's at all possible to prevent regressing here I think it'd be worthwhile.
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.
Not necessarily in scope for this PR: but having log points on Drop
and cancel
would be useful as well. Being able to trace task lifecycles is a useful capability to expose.
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.
Now I remember why I didn't add it, because it is a pain in the current setup :( Will give it some more thought
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.
note that tracing-futures gives you good possibilities as well to track task lifetime if desired.
/// Spawns a task and waits for it to finish. | ||
#[cfg(target_os = "unknown")] | ||
pub fn block_on<F, T>(future: F) | ||
where | ||
F: Future<Output = T>, | ||
F: Future<Output = T> + 'static, | ||
T: 'static, | ||
{ | ||
thread_local! { | ||
// May hold a pre-allocated parker that can be reused for efficiency. | ||
// | ||
// Note that each invocation of `block` needs its own parker. In particular, if `block` | ||
// recursively calls itself, we must make sure that each recursive call uses a distinct | ||
// parker instance. | ||
static CACHE: Cell<Option<Arc<Parker>>> = Cell::new(None); | ||
} | ||
|
||
// Virtual table for wakers based on `Arc<Parker>`. | ||
static VTABLE: RawWakerVTable = { | ||
unsafe fn clone_raw(ptr: *const ()) -> RawWaker { | ||
let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); | ||
#[allow(clippy::redundant_clone)] | ||
mem::forget(arc.clone()); | ||
RawWaker::new(ptr, &VTABLE) | ||
} | ||
|
||
unsafe fn wake_raw(ptr: *const ()) { | ||
let arc = Arc::from_raw(ptr as *const Parker); | ||
arc.unparker().unpark(); | ||
} | ||
|
||
unsafe fn wake_by_ref_raw(ptr: *const ()) { | ||
let arc = ManuallyDrop::new(Arc::from_raw(ptr as *const Parker)); | ||
arc.unparker().unpark(); | ||
} | ||
|
||
unsafe fn drop_raw(ptr: *const ()) { | ||
drop(Arc::from_raw(ptr as *const Parker)) | ||
} | ||
|
||
RawWakerVTable::new(clone_raw, wake_raw, wake_by_ref_raw, drop_raw) | ||
}; | ||
|
||
// Pin the future on the stack. | ||
pin_utils::pin_mut!(future); | ||
|
||
CACHE.with(|cache| { | ||
// Reuse a cached parker or create a new one for this invocation of `block`. | ||
let arc_parker: Arc<Parker> = cache.take().unwrap_or_else(|| Arc::new(Parker::new())); | ||
let ptr = (&*arc_parker as *const Parker) as *const (); | ||
|
||
// Create a waker and task context. | ||
let waker = unsafe { ManuallyDrop::new(Waker::from_raw(RawWaker::new(ptr, &VTABLE))) }; | ||
let cx = &mut Context::from_waker(&waker); | ||
|
||
loop { | ||
if let Poll::Ready(t) = future.as_mut().poll(cx) { | ||
// Save the parker for the next invocation of `block`. | ||
cache.set(Some(arc_parker)); | ||
return t; | ||
} | ||
|
||
arc_parker.park(); | ||
} | ||
}) | ||
Builder::new().local(future).unwrap(); |
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.
it doesn't seem to wait for the task to finish
There is a new runtime in town called smol, you will hear more in the coming days from @stjepang.
This PR drops the internal runtime and instead use smol. Based on this, this PR brings us the following features
spawn_local
support, as smol already implemented it, it was easy to addwasm
support, compiles now out of the box for wasm. This was much easier to add, as the runtime already was isolated.