Skip to content

Commit ef42783

Browse files
committed
Add method to terminate a worker
1 parent 17b01da commit ef42783

File tree

2 files changed

+134
-65
lines changed

2 files changed

+134
-65
lines changed

crates/worker/src/actor/bridge.rs

+127-19
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,101 @@ use std::rc::Weak;
88
use serde::{Deserialize, Serialize};
99

1010
use super::handler_id::HandlerId;
11+
use super::messages::FromWorker;
1112
use super::messages::ToWorker;
13+
use super::native_worker::DedicatedWorker;
1214
use super::native_worker::NativeWorkerExt;
1315
use super::traits::Worker;
14-
use super::{Callback, Shared};
16+
use super::Callback;
1517
use crate::codec::Codec;
1618

1719
pub(crate) type ToWorkerQueue<W> = Vec<ToWorker<W>>;
1820
pub(crate) type CallbackMap<W> = HashMap<HandlerId, Weak<dyn Fn(<W as Worker>::Output)>>;
1921

20-
struct WorkerBridgeInner<W>
22+
pub(crate) struct WorkerBridgeInner<W>
2123
where
2224
W: Worker,
2325
{
2426
// When worker is loaded, queue becomes None.
25-
pending_queue: Shared<Option<ToWorkerQueue<W>>>,
26-
callbacks: Shared<CallbackMap<W>>,
27-
post_msg: Rc<dyn Fn(ToWorker<W>)>,
27+
pending_queue: RefCell<Option<ToWorkerQueue<W>>>,
28+
callbacks: RefCell<CallbackMap<W>>,
29+
native_worker: RefCell<Option<DedicatedWorker>>,
30+
post_msg: Box<dyn Fn(&DedicatedWorker, ToWorker<W>)>,
31+
}
32+
33+
impl<W> WorkerBridgeInner<W>
34+
where
35+
W: Worker + 'static,
36+
{
37+
pub(crate) fn new<CODEC>(native_worker: DedicatedWorker, callbacks: CallbackMap<W>) -> Rc<Self>
38+
where
39+
CODEC: Codec,
40+
W::Input: Serialize + for<'de> Deserialize<'de>,
41+
W::Output: Serialize + for<'de> Deserialize<'de>,
42+
{
43+
let worker = native_worker.clone();
44+
45+
let pending_queue = RefCell::new(Some(Vec::new()));
46+
let callbacks = RefCell::new(callbacks);
47+
let native_worker = RefCell::new(Some(native_worker));
48+
let post_msg = move |worker: &DedicatedWorker, msg: ToWorker<W>| {
49+
worker.post_packed_message::<_, CODEC>(msg)
50+
};
51+
52+
let self_ = Self {
53+
pending_queue,
54+
callbacks,
55+
native_worker,
56+
post_msg: Box::new(post_msg),
57+
};
58+
let self_ = Rc::new(self_);
59+
60+
let handler = {
61+
let bridge_inner = Rc::downgrade(&self_);
62+
// If all bridges are dropped then `self_` is dropped and `upgrade` returns `None`.
63+
move |msg: FromWorker<W>| {
64+
if let Some(bridge_inner) = Weak::upgrade(&bridge_inner) {
65+
match msg {
66+
FromWorker::WorkerLoaded => {
67+
// Set pending queue to `None`. Unless `WorkerLoaded` is
68+
// sent twice, this will always be `Some`.
69+
if let Some(pending_queue) = bridge_inner.take_queue() {
70+
// Will be `None` if the worker has been terminated.
71+
if let Some(worker) =
72+
bridge_inner.native_worker.borrow_mut().as_ref()
73+
{
74+
// Send all pending messages.
75+
for to_worker in pending_queue.into_iter() {
76+
(bridge_inner.post_msg)(worker, to_worker);
77+
}
78+
}
79+
}
80+
}
81+
FromWorker::ProcessOutput(id, output) => {
82+
let mut callbacks = bridge_inner.callbacks.borrow_mut();
83+
84+
if let Some(m) = callbacks.get(&id) {
85+
if let Some(m) = Weak::upgrade(m) {
86+
m(output);
87+
} else {
88+
// The bridge has been dropped.
89+
callbacks.remove(&id);
90+
}
91+
}
92+
}
93+
}
94+
}
95+
}
96+
};
97+
98+
worker.set_on_packed_message::<_, CODEC, _>(handler);
99+
100+
self_
101+
}
102+
103+
fn take_queue(&self) -> Option<ToWorkerQueue<W>> {
104+
self.pending_queue.borrow_mut().take()
105+
}
28106
}
29107

30108
impl<W> fmt::Debug for WorkerBridgeInner<W>
@@ -49,10 +127,24 @@ where
49127
m.push(msg);
50128
}
51129
None => {
52-
(self.post_msg)(msg);
130+
if let Some(worker) = self.native_worker.borrow().as_ref() {
131+
(self.post_msg)(worker, msg);
132+
}
53133
}
54134
}
55135
}
136+
137+
/// Terminate the worker, no more messages can be sent after this.
138+
fn terminate(&self) {
139+
if let Some(worker) = self.native_worker.borrow_mut().take() {
140+
worker.terminate();
141+
}
142+
}
143+
144+
/// Returns true if the worker is terminated.
145+
fn is_terminated(&self) -> bool {
146+
self.native_worker.borrow().is_none()
147+
}
56148
}
57149

58150
impl<W> Drop for WorkerBridgeInner<W>
@@ -66,6 +158,15 @@ where
66158
}
67159

68160
/// A connection manager for components interaction with workers.
161+
///
162+
/// Dropping this object will send a disconnect message to the worker and drop
163+
/// the callback if set, but will have no effect on forked bridges. Note that
164+
/// the worker will still receive and process any messages sent over the bridge
165+
/// up to that point, but the reply will not trigger a callback. If all forked
166+
/// bridges for a worker are dropped, the worker will be sent a destroy message.
167+
///
168+
/// To terminate the worker and stop execution immediately, use
169+
/// [`terminate`](#method.terminate).
69170
pub struct WorkerBridge<W>
70171
where
71172
W: Worker,
@@ -84,26 +185,16 @@ where
84185
self.inner.send_message(ToWorker::Connected(self.id));
85186
}
86187

87-
pub(crate) fn new<CODEC>(
188+
pub(crate) fn new(
88189
id: HandlerId,
89-
native_worker: web_sys::Worker,
90-
pending_queue: Rc<RefCell<Option<ToWorkerQueue<W>>>>,
91-
callbacks: Rc<RefCell<CallbackMap<W>>>,
190+
inner: Rc<WorkerBridgeInner<W>>,
92191
callback: Option<Callback<W::Output>>,
93192
) -> Self
94193
where
95-
CODEC: Codec,
96194
W::Input: Serialize + for<'de> Deserialize<'de>,
97195
{
98-
let post_msg = move |msg: ToWorker<W>| native_worker.post_packed_message::<_, CODEC>(msg);
99-
100196
let self_ = Self {
101-
inner: WorkerBridgeInner {
102-
pending_queue,
103-
callbacks,
104-
post_msg: Rc::new(post_msg),
105-
}
106-
.into(),
197+
inner,
107198
id,
108199
_worker: PhantomData,
109200
_cb: callback,
@@ -146,6 +237,23 @@ where
146237

147238
self_
148239
}
240+
241+
/// Immediately terminates the worker and stops any execution in progress,
242+
/// for this and all forked bridges. All messages will be dropped without
243+
/// the worker receiving them. No disconnect or destroy message is sent. Any
244+
/// messages sent after this point are dropped (from this bridge or any
245+
/// forks).
246+
///
247+
/// For more details see
248+
/// [`web_sys::Worker::terminate`](https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Worker.html#method.terminate).
249+
pub fn terminate(&self) {
250+
self.inner.terminate()
251+
}
252+
253+
/// Returns true if the worker is terminated.
254+
pub fn is_terminated(&self) -> bool {
255+
self.inner.is_terminated()
256+
}
149257
}
150258

151259
impl<W> Drop for WorkerBridge<W>

crates/worker/src/actor/spawner.rs

+7-46
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,19 @@
1-
use std::cell::RefCell;
21
use std::collections::HashMap;
32
use std::fmt;
43
use std::marker::PhantomData;
5-
use std::rc::{Rc, Weak};
4+
use std::rc::Rc;
65

76
use gloo_utils::window;
87
use js_sys::Array;
98
use serde::de::Deserialize;
109
use serde::ser::Serialize;
1110
use web_sys::{Blob, BlobPropertyBag, Url};
1211

13-
use super::bridge::{CallbackMap, WorkerBridge};
12+
use super::bridge::{WorkerBridge, WorkerBridgeInner};
1413
use super::handler_id::HandlerId;
15-
use super::messages::FromWorker;
16-
use super::native_worker::{DedicatedWorker, NativeWorkerExt};
14+
use super::native_worker::DedicatedWorker;
1715
use super::traits::Worker;
18-
use super::{Callback, Shared};
16+
use super::Callback;
1917
use crate::codec::{Bincode, Codec};
2018

2119
fn create_worker(path: &str) -> DedicatedWorker {
@@ -110,53 +108,16 @@ where
110108
W::Input: Serialize + for<'de> Deserialize<'de>,
111109
W::Output: Serialize + for<'de> Deserialize<'de>,
112110
{
113-
let pending_queue = Rc::new(RefCell::new(Some(Vec::new())));
114111
let handler_id = HandlerId::new();
115112
let mut callbacks = HashMap::new();
116113

117114
if let Some(m) = self.callback.as_ref().map(Rc::downgrade) {
118115
callbacks.insert(handler_id, m);
119116
}
120117

121-
let callbacks: Shared<CallbackMap<W>> = Rc::new(RefCell::new(callbacks));
122-
123-
let handler = {
124-
let pending_queue = pending_queue.clone();
125-
let callbacks = callbacks.clone();
126-
127-
let worker = worker.clone();
128-
129-
move |msg: FromWorker<W>| match msg {
130-
FromWorker::WorkerLoaded => {
131-
if let Some(pending_queue) = pending_queue.borrow_mut().take() {
132-
for to_worker in pending_queue.into_iter() {
133-
worker.post_packed_message::<_, CODEC>(to_worker);
134-
}
135-
}
136-
}
137-
FromWorker::ProcessOutput(id, output) => {
138-
let mut callbacks = callbacks.borrow_mut();
139-
140-
if let Some(m) = callbacks.get(&id) {
141-
if let Some(m) = Weak::upgrade(m) {
142-
m(output);
143-
} else {
144-
callbacks.remove(&id);
145-
}
146-
}
147-
}
148-
}
149-
};
150-
151-
worker.set_on_packed_message::<_, CODEC, _>(handler);
152-
153-
WorkerBridge::<W>::new::<CODEC>(
154-
handler_id,
155-
worker,
156-
pending_queue,
157-
callbacks,
158-
self.callback.clone(),
159-
)
118+
let inner = WorkerBridgeInner::<W>::new::<CODEC>(worker, callbacks);
119+
120+
WorkerBridge::<W>::new(handler_id, inner, self.callback.clone())
160121
}
161122

162123
/// Spawns a Worker.

0 commit comments

Comments
 (0)