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

Add option to spawn workers as a module #421

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions crates/worker/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ features = [
"Url",
"Worker",
"WorkerOptions",
"WorkerType",
]

[features]
Expand Down
122 changes: 79 additions & 43 deletions crates/worker/src/actor/spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use gloo_utils::window;
use js_sys::Array;
use serde::de::Deserialize;
use serde::ser::Serialize;
use web_sys::{Blob, BlobPropertyBag, Url};
use web_sys::{Blob, BlobPropertyBag, Url, WorkerOptions, WorkerType};

use super::bridge::{CallbackMap, WorkerBridge};
use super::handler_id::HandlerId;
Expand All @@ -18,28 +18,6 @@ use super::traits::Worker;
use super::{Callback, Shared};
use crate::codec::{Bincode, Codec};

fn create_worker(path: &str) -> DedicatedWorker {
let js_shim_url = Url::new_with_base(
path,
&window().location().href().expect("failed to read href."),
)
.expect("failed to create url for javascript entrypoint")
.to_string();

let wasm_url = js_shim_url.replace(".js", "_bg.wasm");

let array = Array::new();
array.push(&format!(r#"importScripts("{js_shim_url}");wasm_bindgen("{wasm_url}");"#).into());
let blob = Blob::new_with_str_sequence_and_options(
&array,
BlobPropertyBag::new().type_("application/javascript"),
)
.unwrap();
let url = Url::create_object_url_with_blob(&blob).unwrap();

DedicatedWorker::new(&url).expect("failed to spawn worker")
}

/// A spawner to create workers.
#[derive(Clone)]
pub struct WorkerSpawner<W, CODEC = Bincode>
Expand All @@ -49,6 +27,8 @@ where
{
_marker: PhantomData<(W, CODEC)>,
callback: Option<Callback<W::Output>>,
with_loader: bool,
as_module: bool,
}

impl<W, CODEC> fmt::Debug for WorkerSpawner<W, CODEC>
Expand Down Expand Up @@ -81,6 +61,8 @@ where
Self {
_marker: PhantomData,
callback: None,
with_loader: false,
as_module: true,
}
}

Expand All @@ -92,6 +74,8 @@ where
WorkerSpawner {
_marker: PhantomData,
callback: self.callback.clone(),
with_loader: self.with_loader,
as_module: self.as_module,
}
}

Expand All @@ -105,6 +89,41 @@ where
self
}

/// Indicates that [`spawn`](WorkerSpawner#method.spawn) should expect a
/// `path` to a loader shim script (e.g. when using Trunk, created by using
/// the [`data-loader-shim`](https://trunkrs.dev/assets/#link-asset-types)
/// asset type) and one does not need to be generated. `false` by default.
pub fn with_loader(&mut self, with_loader: bool) -> &mut Self {
self.with_loader = with_loader;

self
}

/// Determines whether the worker will be spawned with
/// [`options.type`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#type)
/// set to `module`. `true` by default.
///
/// This option should be un-set if the worker was created with the
/// `--target no-modules` flag of `wasm-bindgen`. If using Trunk, see the
/// [`data-bindgen-target`](https://trunkrs.dev/assets/#link-asset-types)
/// asset type.
pub fn as_module(&mut self, as_module: bool) -> &mut Self {
self.as_module = as_module;

self
}

/// Spawns a Worker.
pub fn spawn(&self, path: &str) -> WorkerBridge<W>
where
W::Input: Serialize + for<'de> Deserialize<'de>,
W::Output: Serialize + for<'de> Deserialize<'de>,
{
let worker = self.create_worker(path).expect("failed to spawn worker");

self.spawn_inner(worker)
}

fn spawn_inner(&self, worker: DedicatedWorker) -> WorkerBridge<W>
where
W::Input: Serialize + for<'de> Deserialize<'de>,
Expand Down Expand Up @@ -159,25 +178,42 @@ where
)
}

/// Spawns a Worker.
pub fn spawn(&self, path: &str) -> WorkerBridge<W>
where
W::Input: Serialize + for<'de> Deserialize<'de>,
W::Output: Serialize + for<'de> Deserialize<'de>,
{
let worker = create_worker(path);

self.spawn_inner(worker)
}

/// Spawns a Worker with a loader shim script.
pub fn spawn_with_loader(&self, loader_path: &str) -> WorkerBridge<W>
where
W::Input: Serialize + for<'de> Deserialize<'de>,
W::Output: Serialize + for<'de> Deserialize<'de>,
{
let worker = DedicatedWorker::new(loader_path).expect("failed to spawn worker");

self.spawn_inner(worker)
fn create_worker(&self, path: &str) -> Option<DedicatedWorker> {
let path = if self.with_loader {
std::borrow::Cow::Borrowed(path)
} else {
let js_shim_url = Url::new_with_base(
path,
&window().location().href().expect("failed to read href."),
)
.expect("failed to create url for javascript entrypoint")
.to_string();

let wasm_url = js_shim_url.replace(".js", "_bg.wasm");

let array = Array::new();
let shim = if self.as_module {
format!(r#"import init from '{js_shim_url}';await init();"#)
} else {
format!(r#"importScripts("{js_shim_url}");wasm_bindgen("{wasm_url}");"#)
};
array.push(&shim.into());
let blob = Blob::new_with_str_sequence_and_options(
&array,
BlobPropertyBag::new().type_("application/javascript"),
)
.unwrap();
let url = Url::create_object_url_with_blob(&blob).unwrap();
std::borrow::Cow::Owned(url)
};
let path = path.as_ref();

if self.as_module {
let mut options = WorkerOptions::new();
options.type_(WorkerType::Module);
DedicatedWorker::new_with_options(path, &options).ok()
} else {
DedicatedWorker::new(path).ok()
}
}
}
35 changes: 23 additions & 12 deletions crates/worker/src/oneshot/spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,28 +39,39 @@ where
}
}

/// Spawns an Oneshot Worker.
pub fn spawn(mut self, path: &str) -> OneshotBridge<N>
where
N::Input: Serialize + for<'de> Deserialize<'de>,
N::Output: Serialize + for<'de> Deserialize<'de>,
{
let rx = OneshotBridge::register_callback(&mut self.inner);
/// Indicates that [`spawn`](WorkerSpawner#method.spawn) should expect a
/// `path` to a loader shim script (e.g. when using Trunk, created by using
/// the [`data-loader-shim`](https://trunkrs.dev/assets/#link-asset-types)
/// asset type) and one does not need to be generated. `false` by default.
pub fn with_loader(mut self, with_loader: bool) -> Self {
self.inner.with_loader(with_loader);

let inner = self.inner.spawn(path);
self
}

OneshotBridge::new(inner, rx)
/// Determines whether the worker will be spawned with
/// [`options.type`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#type)
/// set to `module`. `true` by default.
///
/// This option should be un-set if the worker was created with the
/// `--target no-modules` flag of `wasm-bindgen`. If using Trunk, see the
/// [`data-bindgen-target`](https://trunkrs.dev/assets/#link-asset-types)
/// asset type.
pub fn as_module(mut self, as_module: bool) -> Self {
self.inner.as_module(as_module);

self
}

/// Spawns an Oneshot Worker with a loader shim script.
pub fn spawn_with_loader(mut self, loader_path: &str) -> OneshotBridge<N>
/// Spawns a Oneshot Worker.
pub fn spawn(mut self, path: &str) -> OneshotBridge<N>
where
N::Input: Serialize + for<'de> Deserialize<'de>,
N::Output: Serialize + for<'de> Deserialize<'de>,
{
let rx = OneshotBridge::register_callback(&mut self.inner);

let inner = self.inner.spawn_with_loader(loader_path);
let inner = self.inner.spawn(path);

OneshotBridge::new(inner, rx)
}
Expand Down
35 changes: 23 additions & 12 deletions crates/worker/src/reactor/spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,28 +40,39 @@ where
}
}

/// Spawns a reactor worker.
pub fn spawn(mut self, path: &str) -> ReactorBridge<R>
where
<R::Scope as ReactorScoped>::Input: Serialize + for<'de> Deserialize<'de>,
<R::Scope as ReactorScoped>::Output: Serialize + for<'de> Deserialize<'de>,
{
let rx = ReactorBridge::register_callback(&mut self.inner);
/// Indicates that [`spawn`](WorkerSpawner#method.spawn) should expect a
/// `path` to a loader shim script (e.g. when using Trunk, created by using
/// the [`data-loader-shim`](https://trunkrs.dev/assets/#link-asset-types)
/// asset type) and one does not need to be generated. `false` by default.
pub fn with_loader(mut self, with_loader: bool) -> Self {
self.inner.with_loader(with_loader);

let inner = self.inner.spawn(path);
self
}

ReactorBridge::new(inner, rx)
/// Determines whether the worker will be spawned with
/// [`options.type`](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#type)
/// set to `module`. `true` by default.
///
/// This option should be un-set if the worker was created with the
/// `--target no-modules` flag of `wasm-bindgen`. If using Trunk, see the
/// [`data-bindgen-target`](https://trunkrs.dev/assets/#link-asset-types)
/// asset type.
pub fn as_module(mut self, as_module: bool) -> Self {
self.inner.as_module(as_module);

self
}

/// Spawns a Reactor Worker with a loader shim script.
pub fn spawn_with_loader(mut self, loader_path: &str) -> ReactorBridge<R>
/// Spawns a reactor worker.
pub fn spawn(mut self, path: &str) -> ReactorBridge<R>
where
<R::Scope as ReactorScoped>::Input: Serialize + for<'de> Deserialize<'de>,
<R::Scope as ReactorScoped>::Output: Serialize + for<'de> Deserialize<'de>,
{
let rx = ReactorBridge::register_callback(&mut self.inner);

let inner = self.inner.spawn_with_loader(loader_path);
let inner = self.inner.spawn(path);

ReactorBridge::new(inner, rx)
}
Expand Down
2 changes: 1 addition & 1 deletion examples/file-hash/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>File Hasher</title>

<link data-trunk rel="rust" data-type="worker" data-wasm-opt="z" data-bin="example_file_hash_worker" data-weak-refs data-loader-shim />
<link data-trunk rel="rust" data-type="worker" data-wasm-opt="z" data-bin="example_file_hash_worker" data-weak-refs data-loader-shim data-bindgen-target="web" />
<link data-trunk rel="rust" data-wasm-opt="z" data-bin="example_file_hash_app" />
</head>
</html>
3 changes: 2 additions & 1 deletion examples/file-hash/src/bin/example_file_hash_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ fn App() -> Html {
result.set(Some(o.hash));
})
.encoding::<TransferrableCodec>()
.spawn_with_loader("/example_file_hash_worker_loader.js")
.with_loader(true)
.spawn("/example_file_hash_worker_loader.js")
},
(),
)
Expand Down
2 changes: 1 addition & 1 deletion examples/markdown/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Markdown</title>

<link data-trunk rel="rust" data-type="worker" data-wasm-opt="z" data-bin="example_markdown_worker" data-weak-refs data-loader-shim />
<link data-trunk rel="rust" data-type="worker" data-wasm-opt="z" data-bin="example_markdown_worker" data-weak-refs data-loader-shim data-bindgen-target="web" />
<link data-trunk rel="rust" data-wasm-opt="z" data-bin="example_markdown_app" />
</head>

Expand Down
5 changes: 3 additions & 2 deletions examples/markdown/src/bin/example_markdown_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ fn main() {
.flatten()
.expect_throw("failed to query root element");

let mut bridge =
MarkdownWorker::spawner().spawn_with_loader("/example_markdown_worker_loader.js");
let mut bridge = MarkdownWorker::spawner()
.with_loader(true)
.spawn("/example_markdown_worker_loader.js");

spawn_local(async move {
let content = bridge.run(MARKDOWN_CONTENT.to_owned()).await;
Expand Down
2 changes: 1 addition & 1 deletion examples/prime/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prime Calculator</title>

<link data-trunk rel="rust" data-type="worker" data-wasm-opt="z" data-bin="example_prime_worker" data-weak-refs data-loader-shim />
<link data-trunk rel="rust" data-type="worker" data-wasm-opt="z" data-bin="example_prime_worker" data-weak-refs data-loader-shim data-bindgen-target="web" />
<link data-trunk rel="rust" data-wasm-opt="z" data-bin="example_prime_app" />
<style>
.result-item {
Expand Down
3 changes: 2 additions & 1 deletion examples/prime/src/bin/example_prime_app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ fn main() {
let started = Rc::new(Cell::new(false));

let (bridge_sink, mut bridge_stream) = Prime::spawner()
.spawn_with_loader("/example_prime_worker_loader.js")
.with_loader(true)
.spawn("/example_prime_worker_loader.js")
.split();

{
Expand Down