Skip to content
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

Merged
merged 26 commits into from
May 7, 2020
Merged

Conversation

dignifiedquire
Copy link
Member

@dignifiedquire dignifiedquire commented Apr 26, 2020

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

  • Speed, I will post more benchmarks soon, but all things are pointing at considerable speed improvements
  • spawn_local support, as smol already implemented it, it was easy to add
  • wasm support, compiles now out of the box for wasm. This was much easier to add, as the runtime already was isolated.

src/task/spawn_local.rs Outdated Show resolved Hide resolved
@skade
Copy link
Collaborator

skade commented Apr 26, 2020

I'm a bit hesitant on adding spawn_local straight away. We have a slow unstructured creep on the runtime interface without having a written direction where we are going.

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.

@dignifiedquire
Copy link
Member Author

@skade how about we mark spawn_local as unstable as compromise? So far #457 hasn't gotten much attention and the way it works right now, it at least mirrors the way spawn works nicely. Not having spawn_local has been stopping me from using async-std in multiple projects already.

@ghost ghost mentioned this pull request Apr 27, 2020
@dignifiedquire
Copy link
Member Author

@k-nasa do you have any idea why the no-std build is failing. It seems to be that futures-core/alloc doesn't compile anymore, but I didn't change anything in that regard asfaik.

error[E0463]: can't find crate for `std`
##[error]  |
  = note: the `thumbv7m-none-eabi` target may not be installed

error: aborting due to previous error
##[error]aborting due to previous error
For more information about this error, try `rustc --explain E0463`.
error: could not compile `futures-core`.
##[error]could not compile `futures-core`.
To learn more, run the command again with --verbose.
##[error]The process '/usr/share/rust/.cargo/bin/cargo' failed with exit code 101

@k-nasa
Copy link
Member

k-nasa commented Apr 28, 2020

@dignifiedquire I researched 🔍

Try the following command.
I think you can probably build it.

cargo build --no-default-features --features alloc --target thumbv7m-none-eabi -Z avoid-dev-deps -Zfeatures=itarget

It seems to me that this is the part that caused the build to fail. By commenting out, you can build without adding -Zfeatures=itarget. Perhaps the problem is that wasm-timer and futures-channel contain futures-core/std.

[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-timer = "0.2.4"
wasm-bindgen-futures = "0.4.10"
futures-channel = "0.3.4"

rust-lang/cargo#7914

@dignifiedquire
Copy link
Member Author

@k-nasa thanks, those dependencies where the issue, managed to fix it

@dignifiedquire dignifiedquire marked this pull request as ready for review May 2, 2020 18:27
@skade
Copy link
Collaborator

skade commented May 6, 2020

After chatting with @dignifiedquire, I want to retract my hesitation to add spawn_local.

Copy link
Contributor

@yoshuawuyts yoshuawuyts left a 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 }
Copy link
Contributor

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.

Copy link
Member Author

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(())
})
});
Copy link
Contributor

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?

Copy link
Member Author

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()
Copy link
Contributor

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.

src/net/mod.rs Show resolved Hide resolved
Comment on lines +79 to +80
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
wasm-bindgen-test = "0.3.10"
Copy link
Contributor

Choose a reason for hiding this comment

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

This is so exciting!

Comment on lines -39 to -42
// Log this `spawn` operation.
trace!("spawn", {
task_id: task.id().0,
parent_task_id: Task::get_current(|t| t.id().0).unwrap_or(0),
Copy link
Contributor

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.

Copy link
Contributor

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.

Copy link
Member Author

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

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.

src/task/join_handle.rs Show resolved Hide resolved
src/task/yield_now.rs Show resolved Hide resolved
@ghost ghost mentioned this pull request May 7, 2020
Comment on lines +36 to +43
/// 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();
Copy link

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

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.

6 participants