Implement PrivateAgent (#2318)

* Implement PrivateAgent, Separate Threaded into PublicAgent & PrivateAgent.

* Add migration docs.

* Update docs.
This commit is contained in:
Kaede Hoshikawa 2022-01-02 03:45:11 +09:00 committed by GitHub
parent c46cda1e6f
commit 6f6519dab4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 27 deletions

View File

@ -1,5 +1,5 @@
use agents::native_worker::Worker; use agents::native_worker::Worker;
use yew_agent::Threaded; use yew_agent::PublicAgent;
fn main() { fn main() {
wasm_logger::init(wasm_logger::Config::default()); wasm_logger::init(wasm_logger::Config::default());

View File

@ -5,7 +5,7 @@ pub mod agent;
pub mod app; pub mod app;
use app::Model; use app::Model;
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
use yew_agent::Threaded; use yew_agent::PublicAgent;
#[wasm_bindgen(start)] #[wasm_bindgen(start)]
pub fn start() { pub fn start() {

View File

@ -1,4 +1,4 @@
//! This module contains types to support multi-threading and state management. //! This module contains Yew's web worker implementation.
mod hooks; mod hooks;
mod link; mod link;
@ -10,7 +10,7 @@ pub use link::AgentLink;
pub(crate) use link::*; pub(crate) use link::*;
pub(crate) use pool::*; pub(crate) use pool::*;
pub use pool::{Dispatched, Dispatcher}; pub use pool::{Dispatched, Dispatcher};
pub use worker::{Private, Public, Threaded}; pub use worker::{Private, PrivateAgent, Public, PublicAgent};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::fmt; use std::fmt;

View File

@ -2,8 +2,8 @@ mod private;
mod public; mod public;
mod queue; mod queue;
pub use private::Private; pub use private::{Private, PrivateAgent};
pub use public::Public; pub use public::{Public, PublicAgent};
use super::*; use super::*;
use js_sys::{Array, Reflect, Uint8Array}; use js_sys::{Array, Reflect, Uint8Array};
@ -13,11 +13,19 @@ use web_sys::{
Blob, BlobPropertyBag, DedicatedWorkerGlobalScope, MessageEvent, Url, Worker, WorkerOptions, Blob, BlobPropertyBag, DedicatedWorkerGlobalScope, MessageEvent, Url, Worker, WorkerOptions,
}; };
/// Implements rules to register a worker in a separate thread. pub(crate) struct WorkerResponder {}
pub trait Threaded {
/// Executes an agent in the current environment. impl<AGN> Responder<AGN> for WorkerResponder
/// Uses in `main` function of a worker. where
fn register(); AGN: Agent,
<AGN as Agent>::Input: Serialize + for<'de> Deserialize<'de>,
<AGN as Agent>::Output: Serialize + for<'de> Deserialize<'de>,
{
fn respond(&self, id: HandlerId, output: AGN::Output) {
let msg = FromWorker::ProcessOutput(id, output);
let data = msg.pack();
worker_self().post_message_vec(data);
}
} }
/// Message packager, based on serde::Serialize/Deserialize /// Message packager, based on serde::Serialize/Deserialize

View File

@ -21,6 +21,56 @@ pub struct Private<AGN> {
_agent: PhantomData<AGN>, _agent: PhantomData<AGN>,
} }
/// A trait to enable private agents being registered in a web worker.
pub trait PrivateAgent {
/// Executes an agent in the current environment.
/// Uses in `main` function of a worker.
fn register();
}
impl<AGN> PrivateAgent for AGN
where
AGN: Agent<Reach = Private<AGN>>,
<AGN as Agent>::Input: Serialize + for<'de> Deserialize<'de>,
<AGN as Agent>::Output: Serialize + for<'de> Deserialize<'de>,
{
fn register() {
let scope = AgentScope::<AGN>::new();
let responder = WorkerResponder {};
let link = AgentLink::connect(&scope, responder);
let upd = AgentLifecycleEvent::Create(link);
scope.send(upd);
let handler = move |data: Vec<u8>| {
let msg = ToWorker::<AGN::Input>::unpack(&data);
match msg {
ToWorker::Connected(_id) => {
let upd = AgentLifecycleEvent::Connected(SINGLETON_ID);
scope.send(upd);
}
ToWorker::ProcessInput(_id, value) => {
let upd = AgentLifecycleEvent::Input(value, SINGLETON_ID);
scope.send(upd);
}
ToWorker::Disconnected(_id) => {
let upd = AgentLifecycleEvent::Disconnected(SINGLETON_ID);
scope.send(upd);
}
ToWorker::Destroy => {
let upd = AgentLifecycleEvent::Destroy;
scope.send(upd);
// Terminates web worker
worker_self().close();
}
}
};
let loaded: FromWorker<AGN::Output> = FromWorker::WorkerLoaded;
let loaded = loaded.pack();
let worker = worker_self();
worker.set_onmessage_closure(handler);
worker.post_message_vec(loaded);
}
}
impl<AGN> Discoverer for Private<AGN> impl<AGN> Discoverer for Private<AGN>
where where
AGN: Agent, AGN: Agent,

View File

@ -181,22 +181,14 @@ where
} }
} }
struct WorkerResponder {} /// A trait to enable public agents being registered in a web worker.
pub trait PublicAgent {
impl<AGN> Responder<AGN> for WorkerResponder /// Executes an agent in the current environment.
where /// Uses in `main` function of a worker.
AGN: Agent, fn register();
<AGN as Agent>::Input: Serialize + for<'de> Deserialize<'de>,
<AGN as Agent>::Output: Serialize + for<'de> Deserialize<'de>,
{
fn respond(&self, id: HandlerId, output: AGN::Output) {
let msg = FromWorker::ProcessOutput(id, output);
let data = msg.pack();
worker_self().post_message_vec(data);
}
} }
impl<AGN> Threaded for AGN impl<AGN> PublicAgent for AGN
where where
AGN: Agent<Reach = Public<AGN>>, AGN: Agent<Reach = Public<AGN>>,
<AGN as Agent>::Input: Serialize + for<'de> Deserialize<'de>, <AGN as Agent>::Input: Serialize + for<'de> Deserialize<'de>,

View File

@ -36,8 +36,8 @@ The code can be found in the <desc> tag of the svgs.
When no bridges are connected to this agent, the agent will disappear. When no bridges are connected to this agent, the agent will disappear.
* Private - Spawn a new agent in a web worker for every new bridge. This is good for moving shared but * Private - Spawn a new agent 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. \(TODO verify\) When independent behavior that communicates with the browser out of components. When
the task is done, the agent will disappear. the the connected bridge is dropped, the agent will disappear.
* Global \(WIP\) * Global \(WIP\)

View File

@ -2,8 +2,21 @@
title: "From 0.1.0 to 0.2.0" title: "From 0.1.0 to 0.2.0"
--- ---
## Removal of `Context` and `Job` Agents
The `Context` and `Job` Agents have been removed in favour of Yew's Context API. The `Context` and `Job` Agents have been removed in favour of Yew's Context API.
You can see the updated [`pub_sub`](https://github.com/yewstack/yew/tree/master/examples/pub_sub) example about how to use Context API. You can see the updated [`pub_sub`](https://github.com/yewstack/yew/tree/master/examples/pub_sub) example about how to use Context API.
For users of `yew_agent::utils::store`, you may switch to third party solutions like: [Yewdux](https://github.com/intendednull/yewdux) or [Bounce](https://github.com/futursolo/bounce). For users of `yew_agent::utils::store`, you may switch to third party solutions like: [Yewdux](https://github.com/intendednull/yewdux) or [Bounce](https://github.com/futursolo/bounce).
## `Threaded` is separated into `PublicAgent` and `PrivateAgent`
Replace `use yew_agent::Threaded;` with `use yew_agent::PublicAgent;`.
:::note
`Threaded` was never implemented for Private Agents.
All existing web worker-based agents are Public Agents.
:::