Skip to content

Commit 17cbf2f

Browse files
Vendoring gloo-workers into yew-agent, support module type web worker (#3859)
1 parent 433a0f2 commit 17cbf2f

35 files changed

+2030
-88
lines changed

Cargo.lock

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/yew-agent/Cargo.toml

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,28 @@ rust-version = "1.76.0"
1313

1414
[dependencies]
1515
yew = { version = "0.21.0", path = "../yew" }
16-
gloo-worker = { version = "0.5", features = ["futures"] }
1716
wasm-bindgen = "0.2"
17+
js-sys = "0.3"
18+
pinned = "0.1.0"
19+
thiserror = "1.0.37"
20+
bincode = { version = "1.3.3" }
21+
wasm-bindgen-futures = "0.4"
1822
serde = { version = "1", features = ["derive"] }
1923
futures = "0.3"
2024
yew-agent-macro = { version = "0.2", path = "../yew-agent-macro" }
2125

26+
[dependencies.web-sys]
27+
version = "0.3"
28+
features = [
29+
"Blob",
30+
"BlobPropertyBag",
31+
"DedicatedWorkerGlobalScope",
32+
"MessageEvent",
33+
"Url",
34+
"Worker",
35+
"WorkerOptions",
36+
"WorkerType"
37+
]
38+
2239
[dev-dependencies]
2340
serde = "1.0.218"

packages/yew-agent/README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Yew Agent
2+
3+
This module contains Yew's web worker implementation.
4+
5+
## Types
6+
7+
There're a couple kinds of agents:
8+
9+
### Oneshot
10+
11+
A kind of agent that for each input, a single output is returned.
12+
13+
#### Reactor
14+
15+
A kind of agent that can send many inputs and receive many outputs over a single bridge.
16+
17+
#### Worker
18+
19+
The low-level implementation of agents that provides an actor model and communicates with
20+
multiple bridges.
21+
22+
## Reachability
23+
24+
When an agent is spawned, each agent is associated with a reachability.
25+
26+
### Private
27+
28+
Each time a bridge is created, a new instance
29+
of agent is spawned. This allows parallel computing between agents.
30+
31+
#### Public
32+
33+
Public agents are shared among all children of a provider.
34+
Only 1 instance will be spawned for each public agents provider.
35+
36+
### Provider
37+
38+
Each Agent requires a provider to provide communications and maintain bridges.
39+
All hooks must be called within a provider.
40+
41+
## Communications with Agents
42+
43+
Hooks provides means to communicate with agent instances.
44+
45+
### Bridge
46+
47+
See: [`use_worker_bridge`](worker::use_worker_bridge),
48+
[`use_reactor_bridge`](reactor::use_reactor_bridge)
49+
50+
A bridge takes a callback to receive outputs from agents
51+
and provides a handle to send inputs to agents.
52+
53+
#### Subscription
54+
55+
See: [`use_worker_subscription`](worker::use_worker_subscription),
56+
[`use_reactor_subscription`](reactor::use_reactor_subscription)
57+
58+
Similar to bridges, a subscription produces a handle to send inputs to agents. However, instead
59+
of notifying the receiver with a callback, it collect all outputs into a slice.
60+
61+
#### Runner
62+
63+
See: [`use_oneshot_runner`](oneshot::use_oneshot_runner)
64+
65+
Unlike other agents, oneshot bridges provide a `use_oneshot_runner` hook to execute oneshot
66+
agents on demand.

packages/yew-agent/src/codec.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//! Submodule providing the `Codec` trait and its default implementation using `bincode`.
2+
3+
use js_sys::Uint8Array;
4+
use serde::{Deserialize, Serialize};
5+
use wasm_bindgen::JsValue;
6+
7+
/// Message Encoding and Decoding Format
8+
pub trait Codec {
9+
/// Encode an input to JsValue
10+
fn encode<I>(input: I) -> JsValue
11+
where
12+
I: Serialize;
13+
14+
/// Decode a message to a type
15+
fn decode<O>(input: JsValue) -> O
16+
where
17+
O: for<'de> Deserialize<'de>;
18+
}
19+
20+
/// Default message encoding with [bincode].
21+
#[derive(Debug)]
22+
pub struct Bincode;
23+
24+
impl Codec for Bincode {
25+
fn encode<I>(input: I) -> JsValue
26+
where
27+
I: Serialize,
28+
{
29+
let buf = bincode::serialize(&input).expect("can't serialize an worker message");
30+
Uint8Array::from(buf.as_slice()).into()
31+
}
32+
33+
fn decode<O>(input: JsValue) -> O
34+
where
35+
O: for<'de> Deserialize<'de>,
36+
{
37+
let data = Uint8Array::from(input).to_vec();
38+
bincode::deserialize(&data).expect("can't deserialize an worker message")
39+
}
40+
}

packages/yew-agent/src/lib.rs

Lines changed: 5 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,68 +1,4 @@
1-
//! This module contains Yew's web worker implementation.
2-
//!
3-
//! ## Types
4-
//!
5-
//! There're a couple kinds of agents:
6-
//!
7-
//! #### Oneshot
8-
//!
9-
//! A kind of agent that for each input, a single output is returned.
10-
//!
11-
//! #### Reactor
12-
//!
13-
//! A kind of agent that can send many inputs and receive many outputs over a single bridge.
14-
//!
15-
//! #### Worker
16-
//!
17-
//! The low-level implementation of agents that provides an actor model and communicates with
18-
//! multiple bridges.
19-
//!
20-
//! ## Reachability
21-
//!
22-
//! When an agent is spawned, each agent is associated with a reachability.
23-
//!
24-
//! #### Private
25-
//!
26-
//! Each time a bridge is created, a new instance
27-
//! of agent is spawned. This allows parallel computing between agents.
28-
//!
29-
//! #### Public
30-
//!
31-
//! Public agents are shared among all children of a provider.
32-
//! Only 1 instance will be spawned for each public agents provider.
33-
//!
34-
//! ### Provider
35-
//!
36-
//! Each Agent requires a provider to provide communications and maintain bridges.
37-
//! All hooks must be called within a provider.
38-
//!
39-
//! ## Communications with Agents
40-
//!
41-
//! Hooks provides means to communicate with agent instances.
42-
//!
43-
//! #### Bridge
44-
//!
45-
//! See: [`use_worker_bridge`](worker::use_worker_bridge),
46-
//! [`use_reactor_bridge`](reactor::use_reactor_bridge)
47-
//!
48-
//! A bridge takes a callback to receive outputs from agents
49-
//! and provides a handle to send inputs to agents.
50-
//!
51-
//! #### Subscription
52-
//!
53-
//! See: [`use_worker_subscription`](worker::use_worker_subscription),
54-
//! [`use_reactor_subscription`](reactor::use_reactor_subscription)
55-
//!
56-
//! Similar to bridges, a subscription produces a handle to send inputs to agents. However, instead
57-
//! of notifying the receiver with a callback, it collect all outputs into a slice.
58-
//!
59-
//! #### Runner
60-
//!
61-
//! See: [`use_oneshot_runner`](oneshot::use_oneshot_runner)
62-
//!
63-
//! Unlike other agents, oneshot bridges provide a `use_oneshot_runner` hook to execute oneshot
64-
//! agents on demand.
65-
1+
#![doc = include_str!("../README.md")]
662
#![deny(
673
clippy::all,
684
missing_docs,
@@ -74,12 +10,13 @@
7410

7511
extern crate self as yew_agent;
7612

13+
pub mod codec;
7714
pub mod oneshot;
7815
pub mod reactor;
7916
pub mod worker;
80-
81-
#[doc(inline)]
82-
pub use gloo_worker::{Bincode, Codec, Registrable, Spawnable};
17+
pub use codec::{Bincode, Codec};
18+
pub mod traits;
19+
pub use traits::{Registrable, Spawnable};
8320

8421
mod reach;
8522
pub mod scope_ext;
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use futures::stream::StreamExt;
2+
use pinned::mpsc;
3+
use pinned::mpsc::UnboundedReceiver;
4+
5+
use super::traits::Oneshot;
6+
use super::worker::OneshotWorker;
7+
use crate::codec::Codec;
8+
use crate::worker::{WorkerBridge, WorkerSpawner};
9+
10+
/// A connection manager for components interaction with oneshot workers.
11+
#[derive(Debug)]
12+
pub struct OneshotBridge<N>
13+
where
14+
N: Oneshot + 'static,
15+
{
16+
inner: WorkerBridge<OneshotWorker<N>>,
17+
rx: UnboundedReceiver<N::Output>,
18+
}
19+
20+
impl<N> OneshotBridge<N>
21+
where
22+
N: Oneshot + 'static,
23+
{
24+
#[inline(always)]
25+
pub(crate) fn new(
26+
inner: WorkerBridge<OneshotWorker<N>>,
27+
rx: UnboundedReceiver<N::Output>,
28+
) -> Self {
29+
Self { inner, rx }
30+
}
31+
32+
#[inline(always)]
33+
pub(crate) fn register_callback<CODEC>(
34+
spawner: &mut WorkerSpawner<OneshotWorker<N>, CODEC>,
35+
) -> UnboundedReceiver<N::Output>
36+
where
37+
CODEC: Codec,
38+
{
39+
let (tx, rx) = mpsc::unbounded();
40+
spawner.callback(move |output| {
41+
let _ = tx.send_now(output);
42+
});
43+
44+
rx
45+
}
46+
47+
/// Forks the bridge.
48+
///
49+
/// This method creates a new bridge that can be used to execute tasks on the same worker
50+
/// instance.
51+
pub fn fork(&self) -> Self {
52+
let (tx, rx) = mpsc::unbounded();
53+
let inner = self.inner.fork(Some(move |output| {
54+
let _ = tx.send_now(output);
55+
}));
56+
57+
Self { inner, rx }
58+
}
59+
60+
/// Run the the current oneshot worker once in the current worker instance.
61+
pub async fn run(&mut self, input: N::Input) -> N::Output {
62+
// &mut self guarantees that the bridge will be
63+
// exclusively borrowed during the time the oneshot worker is running.
64+
self.inner.send(input);
65+
66+
// For each bridge, there can only be 1 active task running on the worker instance.
67+
// The next output will be the output for the input that we just sent.
68+
self.rx
69+
.next()
70+
.await
71+
.expect("failed to receive result from worker")
72+
}
73+
}
Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
//! This module provides task agent implementation.
22
3+
mod bridge;
34
mod hooks;
45
mod provider;
6+
mod registrar;
7+
mod spawner;
8+
mod traits;
9+
mod worker;
510

6-
#[doc(inline)]
7-
pub use gloo_worker::oneshot::{Oneshot, OneshotBridge, OneshotRegistrar, OneshotSpawner};
11+
pub use bridge::OneshotBridge;
812
pub use hooks::{use_oneshot_runner, UseOneshotRunnerHandle};
913
pub use provider::OneshotProvider;
1014
pub(crate) use provider::OneshotProviderState;
15+
pub use registrar::OneshotRegistrar;
16+
pub use spawner::OneshotSpawner;
17+
pub use traits::Oneshot;
1118
/// A procedural macro to create oneshot agents.
1219
pub use yew_agent_macro::oneshot;

packages/yew-agent/src/oneshot/provider.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,19 @@ where
9797
children,
9898
path,
9999
lazy,
100+
module,
100101
reach,
101102
} = props.clone();
102103

103104
// Creates a spawning function so Codec is can be erased from contexts.
104105
let spawn_bridge_fn: Rc<dyn Fn() -> OneshotBridge<T>> = {
105106
let path = path.clone();
106-
Rc::new(move || OneshotSpawner::<T>::new().encoding::<C>().spawn(&path))
107+
Rc::new(move || {
108+
OneshotSpawner::<T>::new()
109+
.as_module(module)
110+
.encoding::<C>()
111+
.spawn(&path)
112+
})
107113
};
108114

109115
let state = {

0 commit comments

Comments
 (0)