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

[WIP] wasm32 backend #99

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@ once_cell = "1.3.1"
piper = "0.1.1"
scoped-tls-hkt = "0.1.2"
slab = "0.4.2"
socket2 = { version = "0.3.12", features = ["pair", "unix"] }

[dependencies.tokio]
version = "0.2.19"
default-features = false
features = ["rt-threaded"]
optional = true

[target.'cfg(not(target_arch="wasm32"))'.dependencies]
socket2 = { version = "0.3.12", features = ["pair", "unix"] }

[target.'cfg(unix)'.dependencies]
nix = "0.17.0"

Expand All @@ -54,7 +56,7 @@ criterion = "0.3"
[workspace]
members = [
".",
"examples",
"examples"
]

[[bench]]
Expand Down
18 changes: 18 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,22 +115,40 @@

#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]

#[cfg(not(target_arch = "wasm32"))]
mod async_io;
#[cfg(not(target_arch = "wasm32"))]
mod block_on;
#[cfg(not(target_arch = "wasm32"))]
mod blocking;
#[cfg(not(target_arch = "wasm32"))]
mod context;
#[cfg(not(target_arch = "wasm32"))]
mod io_event;
#[cfg(not(target_arch = "wasm32"))]
mod reactor;
#[cfg(not(target_arch = "wasm32"))]
mod run;
mod task;
#[cfg(not(target_arch = "wasm32"))]
mod thread_local;
#[cfg(not(target_arch = "wasm32"))]
mod throttle;
#[cfg(not(target_arch = "wasm32"))]
mod timer;
#[cfg(target_arch = "wasm32")]
mod web;
#[cfg(not(target_arch = "wasm32"))]
mod work_stealing;

#[cfg(not(target_arch = "wasm32"))]
pub use async_io::Async;
#[cfg(not(target_arch = "wasm32"))]
pub use block_on::block_on;
#[cfg(not(target_arch = "wasm32"))]
pub use blocking::{iter, reader, writer};
#[cfg(not(target_arch = "wasm32"))]
pub use run::run;
pub use task::Task;
#[cfg(not(target_arch = "wasm32"))]
pub use timer::Timer;
56 changes: 51 additions & 5 deletions src/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,21 @@
//!
//! A [`Task`] handle represents a spawned future that is run by the executor.

use std::fmt::Debug;
use std::future::Future;
use std::pin::Pin;
use std::task::{Context, Poll};
use core::fmt::Debug;
use core::future::Future;
use core::pin::Pin;
use core::task::{Context, Poll};

#[cfg(target_arch = "wasm32")]
use core::marker::PhantomData;

#[cfg(not(target_arch = "wasm32"))]
use crate::blocking::BlockingExecutor;
#[cfg(target_arch = "wasm32")]
use crate::web::WebExecutor;
#[cfg(not(target_arch = "wasm32"))]
use crate::thread_local::ThreadLocalExecutor;
#[cfg(not(target_arch = "wasm32"))]
use crate::work_stealing::WorkStealingExecutor;

/// A runnable future, ready for execution.
Expand Down Expand Up @@ -57,7 +65,17 @@ pub(crate) type Runnable = async_task::Task<()>;
/// [`run()`]: crate::run()
#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"]
#[derive(Debug)]
pub struct Task<T>(pub(crate) Option<async_task::JoinHandle<T, ()>>);
#[cfg(not(target_arch = "wasm32"))]
pub struct Task<T>(
pub(crate) Option<async_task::JoinHandle<T, ()>>
);

#[must_use = "tasks get canceled when dropped, use `.detach()` to run them in the background"]
#[derive(Debug)]
#[cfg(target_arch = "wasm32")]
pub struct Task<T>(
pub(crate) PhantomData<T>
);

impl<T: 'static> Task<T> {
/// Spawns a future onto the thread-local executor.
Expand All @@ -76,9 +94,15 @@ impl<T: 'static> Task<T> {
/// ```
///
/// [`run()`]: crate::run()
#[cfg(not(target_arch = "wasm32"))]
pub fn local(future: impl Future<Output = T> + 'static) -> Task<T> {
ThreadLocalExecutor::spawn(future)
}

#[cfg(target_arch = "wasm32")]
pub fn local(future: impl Future<Output = T> + 'static) -> Task<T> {
WebExecutor::spawn(future)
}
}

impl<T: Send + 'static> Task<T> {
Expand All @@ -98,10 +122,16 @@ impl<T: Send + 'static> Task<T> {
/// ```
///
/// [`run()`]: crate::run()
#[cfg(not(target_arch = "wasm32"))]
pub fn spawn(future: impl Future<Output = T> + Send + 'static) -> Task<T> {
WorkStealingExecutor::get().spawn(future)
}

#[cfg(target_arch = "wasm32")]
pub fn spawn(future: impl Future<Output = T> + Send + 'static) -> Task<T> {
Task::local(future)
}

/// Spawns a future onto the blocking executor.
///
/// This future is allowed to block for an indefinite length of time.
Expand Down Expand Up @@ -132,9 +162,15 @@ impl<T: Send + 'static> Task<T> {
/// [`iter()`]: `crate::iter()`
/// [`reader()`]: `crate::reader()`
/// [`writer()`]: `crate::writer()`
#[cfg(not(target_arch = "wasm32"))]
pub fn blocking(future: impl Future<Output = T> + Send + 'static) -> Task<T> {
BlockingExecutor::get().spawn(future)
}

#[cfg(target_arch = "wasm32")]
pub fn blocking(future: impl Future<Output = T> + Send + 'static) -> Task<T> {
Task::local(future)
}
}

impl<T, E> Task<Result<T, E>>
Expand All @@ -160,6 +196,7 @@ where
/// .await;
/// # })
/// ```
#[cfg(not(target_arch="wasm32"))]
pub fn unwrap(self) -> Task<T> {
Task::spawn(async { self.await.unwrap() })
}
Expand All @@ -182,6 +219,7 @@ where
/// .await;
/// # })
/// ```
#[cfg(not(target_arch="wasm32"))]
pub fn expect(self, msg: &str) -> Task<T> {
let msg = msg.to_owned();
Task::spawn(async move { self.await.expect(&msg) })
Expand All @@ -207,9 +245,13 @@ impl Task<()> {
/// .detach();
/// # })
/// ```
#[cfg(not(target_arch="wasm32"))]
pub fn detach(mut self) {
self.0.take().unwrap();
}

#[cfg(target_arch="wasm32")]
pub fn detach(self) { }
}

impl<T> Task<T> {
Expand Down Expand Up @@ -239,6 +281,7 @@ impl<T> Task<T> {
/// task.cancel().await;
/// # })
/// ```
#[cfg(not(target_arch="wasm32"))]
pub async fn cancel(self) -> Option<T> {
// There's a bug in rustdoc causing it to render `mut self` as `__arg0: Self`, so we just
// do `{ self }` here to avoid marking `self` as mutable.
Expand All @@ -248,6 +291,7 @@ impl<T> Task<T> {
}
}

#[cfg(not(target_arch="wasm32"))]
impl<T> Drop for Task<T> {
fn drop(&mut self) {
if let Some(handle) = &self.0 {
Expand All @@ -256,6 +300,7 @@ impl<T> Drop for Task<T> {
}
}

#[cfg(not(target_arch="wasm32"))]
impl<T> Future for Task<T> {
type Output = T;

Expand All @@ -267,6 +312,7 @@ impl<T> Future for Task<T> {
}
}

#[cfg(not(target_arch="wasm32"))]
impl<T> Into<async_task::JoinHandle<T, ()>> for Task<T> {
fn into(mut self) -> async_task::JoinHandle<T, ()> {
self.0
Expand Down
24 changes: 24 additions & 0 deletions src/web.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
//! The thread-local executor.
//!
//! Tasks created by [`Task::local()`] go into this executor on the
//! wasm32 backend.

use core::future::Future;
use core::marker::PhantomData;
use crate::task::Task;

/// An executor for the web environment Thread-local tasks are spawned
/// by calling [`Task::local()`] and their futures do not have to
/// implement [`Send`].
pub(crate) struct WebExecutor {}

impl WebExecutor {

/// Spawns a future onto this executor.
///
/// Returns a [`Task`] handle for the spawned task.
pub fn spawn<T: 'static>(future: impl Future<Output = T> + 'static) -> Task<T> {
Task(PhantomData)
}

}
2 changes: 2 additions & 0 deletions web-example/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/target
/out/node_modules
19 changes: 19 additions & 0 deletions web-example/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[package]
name = "web"
version = "0.1.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[workspace]

[dependencies]
wasm-bindgen = "0.2.38"

[dependencies.smol]
path = ".."

[dependencies.web-sys]
version = "0.3.15"
features = ["console"]
12 changes: 12 additions & 0 deletions web-example/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/sh

set -ex

# Run the `wasm-pack` CLI tool to build and process the Rust wasm file
wasm-pack build -d out/dist

# Finally, package everything up using Webpack and start a server so we can
# browse the result
cd out
npm install
npm run serve
20 changes: 20 additions & 0 deletions web-example/out/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>smol demo</title>
<style>
html, body, canvas {
margin: 0px;
padding: 0px;
width: 100%;
height: 100%;
overflow: hidden;
background-color: black;
}
</style>
</head>
<body>
<script src="index.js"></script>
</body>
</html>
1 change: 1 addition & 0 deletions web-example/out/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import('./dist/web').then(m => m.run()).catch(console.error);
Loading