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 gloo-worker crate #180

Merged
merged 5 commits into from
Jan 3, 2022
Merged
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
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