Skip to content

Commit

Permalink
Add gloo-worker crate (#180)
Browse files Browse the repository at this point in the history
* move yew-agent to gloo-workers

All the code in this commit is moved from the [Yew repository](https://github.com/yewstack/yew) as of commit yewstack/yew@6f6519d

All the credits go to original code authors.

* Improve documentation

The crate level documentation is copied from the Agents docs at yew.rs

* please no duplication warning

* rename: agents -> workers

* fmt
  • Loading branch information
ranile authored Jan 3, 2022
1 parent 622c0bf commit e482f2e
Show file tree
Hide file tree
Showing 10 changed files with 1,260 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ gloo-render = { version = "0.1.0", path = "crates/render" }
gloo-console = { version = "0.2.1", path = "crates/console" }
gloo-utils = { version = "0.1.1", path = "crates/utils" }
gloo-history = { version = "0.1.0", path = "crates/history" }
gloo-worker = { path = "crates/worker" }

[features]
default = []
Expand All @@ -39,4 +40,5 @@ members = [
"crates/console",
"crates/utils",
"crates/history",
"crates/worker",
]
44 changes: 44 additions & 0 deletions crates/worker/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[package]
name = "gloo-worker"
version = "0.1.0"
authors = ["Rust and WebAssembly Working Group"]
edition = "2018"
readme = "README.md"
description = "Convenience crate for working with Web Workers"
repository = "https://github.com/rustwasm/gloo/tree/master/crates/worker"
homepage = "https://github.com/rustwasm/gloo"
license = "MIT OR Apache-2.0"
categories = ["api-bindings", "asynchronous", "wasm"]

[package.metadata.docs.rs]
all-features = true

rustdoc-args = ["--cfg", "docsrs"]


[dependencies]
anymap2 = "0.13"
bincode = "1"
gloo-console = { path = "../console", version = "0.2" }
gloo-utils = { path = "../utils", version = "0.1" }
js-sys = "0.3"
serde = { version = "1", features = ["derive"] }
slab = "0.4"
wasm-bindgen = "0.2"
wasm-bindgen-futures = { version = "0.4", optional = true }

[dependencies.web-sys]
version = "0.3"
features = [
"Blob",
"BlobPropertyBag",
"DedicatedWorkerGlobalScope",
"MessageEvent",
"Url",
"Worker",
"WorkerOptions",
]

[features]
default = []
futures = ["wasm-bindgen-futures"]
158 changes: 158 additions & 0 deletions crates/worker/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//! Workers are a way to offload tasks to web workers. These are run concurrently using
//! [web-workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers).
//!
//! # Types of Workers
//!
//! ## Reaches
//!
//! * Public - There will exist at most one instance of a Public Worker at any given time.
//! Bridges will spawn or connect to an already spawned worker in a web worker.
//! When no bridges are connected to this worker, the worker will disappear.
//!
//! * Private - Spawn a new worker in a web worker for every new bridge. This is good for
//! moving shared but independent behavior that communicates with the browser out of components.
//! When the the connected bridge is dropped, the worker will disappear.
//!
//! # Communicating with workers
//!
//! ## Bridges
//!
//! A bridge allows bi-directional communication between an worker and a component.
//! Bridges also allow workers to communicate with one another.
//!
//! ## Dispatchers
//!
//! A dispatcher allows uni-directional communication between a component and an worker.
//! A dispatcher allows a component to send messages to an worker.
//!
//! # Overhead
//!
//! Workers use web workers (i.e. Private and Public). They incur a serialization overhead on the
//! messages they send and receive. Workers use [bincode](https://!github.com/servo/bincode)
//! to communicate with other browser worker, so the cost is substantially higher
//! than just calling a function.

#![cfg_attr(docsrs, feature(doc_cfg))]

mod link;
mod pool;
mod worker;

pub use link::WorkerLink;
pub(crate) use link::*;
pub(crate) use pool::*;
pub use pool::{Dispatched, Dispatcher};
use std::cell::RefCell;
pub use worker::{Private, PrivateWorker, Public, PublicWorker};

use serde::{Deserialize, Serialize};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;

/// Alias for `Rc<RefCell<T>>`
pub type Shared<T> = Rc<RefCell<T>>;

/// Alias for `Rc<dyn Fn(IN)>`
pub type Callback<IN> = Rc<dyn Fn(IN)>;

/// Declares the behavior of the worker.
pub trait Worker: Sized + 'static {
/// Reach capability of the worker.
type Reach: Discoverer<Worker = Self>;
/// Type of an input message.
type Message;
/// Incoming message type.
type Input;
/// Outgoing message type.
type Output;

/// Creates an instance of an worker.
fn create(link: WorkerLink<Self>) -> Self;

/// This method called on every update message.
fn update(&mut self, msg: Self::Message);

/// This method called on when a new bridge created.
fn connected(&mut self, _id: HandlerId) {}

/// This method called on every incoming message.
fn handle_input(&mut self, msg: Self::Input, id: HandlerId);

/// This method called on when a new bridge destroyed.
fn disconnected(&mut self, _id: HandlerId) {}

/// This method called when the worker is destroyed.
fn destroy(&mut self) {}

/// Represents the name of loading resource for remote workers which
/// have to live in a separate files.
fn name_of_resource() -> &'static str {
"main.js"
}

/// Indicates whether the name of the resource is relative.
///
/// The default implementation returns `false`, which will cause the result
/// returned by [`Self::name_of_resource`] to be interpreted as an absolute
/// URL. If `true` is returned, it will be interpreted as a relative URL.
fn resource_path_is_relative() -> bool {
false
}

/// Signifies if resource is a module.
/// This has pending browser support.
fn is_module() -> bool {
false
}
}

/// Id of responses handler.
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Hash, Clone, Copy)]
pub struct HandlerId(usize, bool);

impl HandlerId {
fn new(id: usize, respondable: bool) -> Self {
HandlerId(id, respondable)
}
fn raw_id(self) -> usize {
self.0
}
/// Indicates if a handler id corresponds to callback in the Worker runtime.
pub fn is_respondable(self) -> bool {
self.1
}
}

/// Determine a visibility of an worker.
#[doc(hidden)]
pub trait Discoverer {
type Worker: Worker;

/// Spawns an worker and returns `Bridge` implementation.
fn spawn_or_join(
_callback: Option<Callback<<Self::Worker as Worker>::Output>>,
) -> Box<dyn Bridge<Self::Worker>>;
}

/// Bridge to a specific kind of worker.
pub trait Bridge<W: Worker> {
/// Send a message to an worker.
fn send(&mut self, msg: W::Input);
}

/// This trait allows registering or getting the address of a worker.
pub trait Bridged: Worker + Sized + 'static {
/// Creates a messaging bridge between a worker and the component.
fn bridge(callback: Callback<Self::Output>) -> Box<dyn Bridge<Self>>;
}

impl<T> Bridged for T
where
T: Worker,
<T as Worker>::Reach: Discoverer<Worker = T>,
{
fn bridge(callback: Callback<Self::Output>) -> Box<dyn Bridge<Self>> {
Self::Reach::spawn_or_join(Some(callback))
}
}
Loading

0 comments on commit e482f2e

Please sign in to comment.