Skip to main content

ferroui_remote_protocol/
transport_connection_wrapper.rs

1use std::collections::VecDeque;
2use std::sync::{Arc, Condvar, Mutex, Weak};
3
4use crate::error::Error;
5use crate::event_stash::EventStash;
6use crate::i_transport::{
7    exception_handler, message_handler, ExceptionHandler, HandlerToken, IFerroRemoteTransportConnection, Message,
8    MessageHandler,
9};
10use crate::task::{Task, TaskCompletionSource};
11
12struct SendOperation {
13    message: Message,
14    tcs: TaskCompletionSource,
15}
16
17/// The fields the original guards with `_lock`.
18struct Queue {
19    send_queue: VecDeque<SendOperation>,
20    worker_is_alive: bool,
21    // The wrapper was dropped: the worker ends when the queue is empty.
22    closed: bool,
23}
24
25struct WorkerState {
26    lock: Mutex<Queue>,
27    // `_signal`: wakes the worker when a message is queued.
28    signal: Condvar,
29}
30
31/// A connection around another one that keeps the events raised before a
32/// handler is subscribed, and that queues the messages to send so that any
33/// number of threads may send at once.
34pub struct TransportConnectionWrapper {
35    conn: Arc<dyn IFerroRemoteTransportConnection>,
36    on_message: Arc<EventStash<Message>>,
37    on_exception: Arc<EventStash<Error>>,
38    worker: Arc<WorkerState>,
39}
40
41impl TransportConnectionWrapper {
42    pub fn new(conn: Arc<dyn IFerroRemoteTransportConnection>) -> Arc<TransportConnectionWrapper> {
43        Arc::new_cyclic(|this: &Weak<TransportConnectionWrapper>| {
44            let transport: Weak<dyn IFerroRemoteTransportConnection> = this.clone();
45            let on_exception = Arc::new(EventStash::<Error>::new(transport.clone(), None));
46            let on_message = {
47                let on_exception = on_exception.clone();
48                Arc::new(EventStash::<Message>::new(transport, Some(Box::new(move |e| on_exception.fire(e)))))
49            };
50            {
51                let on_exception = on_exception.clone();
52                conn.on_exception(exception_handler(move |_, e| on_exception.fire(e.clone())));
53            }
54            {
55                let on_message = on_message.clone();
56                conn.on_message(message_handler(move |_, m| on_message.fire(m.clone())));
57            }
58            TransportConnectionWrapper {
59                conn,
60                on_message,
61                on_exception,
62                worker: Arc::new(WorkerState {
63                    lock: Mutex::new(Queue { send_queue: VecDeque::new(), worker_is_alive: false, closed: false }),
64                    signal: Condvar::new(),
65                }),
66            }
67        })
68    }
69
70    // Deviation (DEVIATIONS.md, Remote protocol): the original worker is an
71    // `async void` loop that awaits a signal and each send; here it is a
72    // thread, started by the first send, that sends one message at a time
73    // and ends when the wrapper has been dropped and the queue is empty.
74    fn worker(conn: Arc<dyn IFerroRemoteTransportConnection>, state: Arc<WorkerState>) {
75        loop {
76            let wi = {
77                let mut queue = state.lock.lock().unwrap();
78                loop {
79                    if let Some(wi) = queue.send_queue.pop_front() {
80                        break Some(wi);
81                    }
82                    if queue.closed {
83                        break None;
84                    }
85                    queue = state.signal.wait(queue).unwrap();
86                }
87            };
88            let Some(wi) = wi else {
89                return;
90            };
91            match conn.send(wi.message).wait() {
92                Ok(()) => wi.tcs.try_set_result(),
93                Err(e) => wi.tcs.try_set_exception(e),
94            };
95        }
96    }
97}
98
99impl IFerroRemoteTransportConnection for TransportConnectionWrapper {
100    fn dispose(&self) {
101        self.conn.dispose()
102    }
103
104    /// Queues the message and returns; the task completes when the worker
105    /// has sent it. Messages are sent in the order they were queued.
106    fn send(&self, data: Message) -> Task {
107        let tcs = TaskCompletionSource::new();
108        let mut queue = self.worker.lock.lock().unwrap();
109        if !queue.worker_is_alive {
110            queue.worker_is_alive = true;
111            let (conn, state) = (self.conn.clone(), self.worker.clone());
112            std::thread::Builder::new()
113                .name("remote-protocol-sender".to_string())
114                .spawn(move || TransportConnectionWrapper::worker(conn, state))
115                .expect("failed to start the sender thread");
116        }
117        queue.send_queue.push_back(SendOperation { message: data, tcs: tcs.clone() });
118        self.worker.signal.notify_one();
119        tcs.task()
120    }
121
122    fn on_message(&self, handler: MessageHandler) -> HandlerToken {
123        self.on_message.add(handler)
124    }
125
126    fn remove_on_message(&self, token: HandlerToken) {
127        self.on_message.remove(token)
128    }
129
130    fn on_exception(&self, handler: ExceptionHandler) -> HandlerToken {
131        self.on_exception.add(handler)
132    }
133
134    fn remove_on_exception(&self, token: HandlerToken) {
135        self.on_exception.remove(token)
136    }
137
138    fn start(&self) {
139        self.conn.start()
140    }
141}
142
143impl Drop for TransportConnectionWrapper {
144    fn drop(&mut self) {
145        self.worker.lock.lock().unwrap().closed = true;
146        self.worker.signal.notify_all();
147    }
148}
149
150// Tests of the port: the upstream project tests the wrapper through the
151// socket transport only (`tests/remote_protocol_tests.rs`).
152#[cfg(test)]
153mod tests {
154    use std::sync::atomic::{AtomicUsize, Ordering};
155    use std::sync::mpsc::channel;
156    use std::time::Duration;
157
158    use super::*;
159    use crate::i_transport::Delegate;
160    use crate::viewport_messages::MeasureViewportMessage;
161
162    const TIMEOUT: Duration = Duration::from_secs(5);
163
164    /// A connection that records what is sent and raises what the test tells
165    /// it to.
166    struct FakeConnection {
167        sent: Mutex<Vec<Message>>,
168        on_message: Mutex<Delegate<Message>>,
169        on_exception: Mutex<Delegate<Error>>,
170        disposed: AtomicUsize,
171        started: AtomicUsize,
172        fail_sends: bool,
173    }
174
175    impl FakeConnection {
176        fn new(fail_sends: bool) -> Arc<FakeConnection> {
177            Arc::new(FakeConnection {
178                sent: Mutex::new(Vec::new()),
179                on_message: Mutex::new(Delegate::new()),
180                on_exception: Mutex::new(Delegate::new()),
181                disposed: AtomicUsize::new(0),
182                started: AtomicUsize::new(0),
183                fail_sends,
184            })
185        }
186
187        fn raise_message(&self, message: Message) {
188            let handlers = self.on_message.lock().unwrap().snapshot();
189            for handler in &handlers {
190                handler(self, &message);
191            }
192        }
193
194        fn raise_exception(&self, e: Error) {
195            let handlers = self.on_exception.lock().unwrap().snapshot();
196            for handler in &handlers {
197                handler(self, &e);
198            }
199        }
200    }
201
202    impl IFerroRemoteTransportConnection for FakeConnection {
203        fn dispose(&self) {
204            self.disposed.fetch_add(1, Ordering::SeqCst);
205        }
206
207        fn send(&self, data: Message) -> Task {
208            if self.fail_sends {
209                return Task::from_result(Err(Error::InvalidOperation("refused".to_string())));
210            }
211            self.sent.lock().unwrap().push(data);
212            Task::completed()
213        }
214
215        fn on_message(&self, handler: MessageHandler) -> HandlerToken {
216            self.on_message.lock().unwrap().add(handler)
217        }
218
219        fn remove_on_message(&self, token: HandlerToken) {
220            self.on_message.lock().unwrap().remove(token);
221        }
222
223        fn on_exception(&self, handler: ExceptionHandler) -> HandlerToken {
224            self.on_exception.lock().unwrap().add(handler)
225        }
226
227        fn remove_on_exception(&self, token: HandlerToken) {
228            self.on_exception.lock().unwrap().remove(token);
229        }
230
231        fn start(&self) {
232            self.started.fetch_add(1, Ordering::SeqCst);
233        }
234    }
235
236    fn measure(width: f64) -> Message {
237        Arc::new(MeasureViewportMessage { width, height: 0.0 })
238    }
239
240    fn width_of(message: &Message) -> f64 {
241        message.downcast_ref::<MeasureViewportMessage>().unwrap().width
242    }
243
244    #[test]
245    fn events_raised_before_a_handler_is_added_are_replayed_to_the_first_handler_in_order() {
246        let inner = FakeConnection::new(false);
247        let wrapper = TransportConnectionWrapper::new(inner.clone());
248        inner.raise_message(measure(1.0));
249        inner.raise_message(measure(2.0));
250        inner.raise_exception(Error::EndOfStream);
251
252        let (sender, received) = channel();
253        wrapper.on_message(message_handler(move |_, m| {
254            let _ = sender.send(width_of(m));
255        }));
256        assert_eq!(received.try_recv(), Ok(1.0));
257        assert_eq!(received.try_recv(), Ok(2.0));
258        assert!(received.try_recv().is_err());
259
260        // A second handler gets no replay; both get what is raised from now on.
261        let (second_sender, second) = channel();
262        wrapper.on_message(message_handler(move |_, m| {
263            let _ = second_sender.send(width_of(m));
264        }));
265        assert!(second.try_recv().is_err());
266        inner.raise_message(measure(3.0));
267        assert_eq!(received.try_recv(), Ok(3.0));
268        assert_eq!(second.try_recv(), Ok(3.0));
269
270        let (error_sender, errors) = channel();
271        wrapper.on_exception(exception_handler(move |_, e| {
272            let _ = error_sender.send(e.clone());
273        }));
274        assert!(matches!(errors.try_recv(), Ok(Error::EndOfStream)));
275        assert!(errors.try_recv().is_err());
276    }
277
278    #[test]
279    fn handlers_receive_the_wrapper_as_the_connection() {
280        let inner = FakeConnection::new(false);
281        let wrapper = TransportConnectionWrapper::new(inner.clone());
282        // A handler answers on the connection it is given: the answer goes
283        // through the queue of the wrapper to the inner connection.
284        wrapper.on_message(message_handler(|connection, m| {
285            connection.send(measure(width_of(m) + 1.0));
286        }));
287        inner.raise_message(measure(1.0));
288        let deadline = std::time::Instant::now() + TIMEOUT;
289        while inner.sent.lock().unwrap().is_empty() {
290            assert!(std::time::Instant::now() < deadline, "the answer was not sent");
291            std::thread::sleep(Duration::from_millis(5));
292        }
293        assert_eq!(width_of(&inner.sent.lock().unwrap()[0]), 2.0);
294    }
295
296    #[test]
297    fn a_removed_handler_is_not_called() {
298        let inner = FakeConnection::new(false);
299        let wrapper = TransportConnectionWrapper::new(inner.clone());
300        let (sender, received) = channel();
301        let token = wrapper.on_message(message_handler(move |_, m| {
302            let _ = sender.send(width_of(m));
303        }));
304        inner.raise_message(measure(1.0));
305        wrapper.remove_on_message(token);
306        // Without a handler the event is stashed again.
307        inner.raise_message(measure(2.0));
308        assert_eq!(received.try_recv(), Ok(1.0));
309        assert!(received.try_recv().is_err());
310    }
311
312    #[test]
313    fn a_panic_of_a_handler_during_the_replay_is_raised_as_an_exception() {
314        let inner = FakeConnection::new(false);
315        let wrapper = TransportConnectionWrapper::new(inner.clone());
316        inner.raise_message(measure(1.0));
317        let (error_sender, errors) = channel();
318        wrapper.on_exception(exception_handler(move |_, e| {
319            let _ = error_sender.send(e.clone());
320        }));
321        wrapper.on_message(message_handler(|_, _| panic!("handler failed")));
322        match errors.try_recv() {
323            Ok(Error::Handler(message)) => assert_eq!(message, "handler failed"),
324            other => panic!("expected the panic of the handler, got {:?}", other),
325        }
326    }
327
328    #[test]
329    fn sends_are_queued_and_sent_in_order() {
330        let inner = FakeConnection::new(false);
331        let wrapper = TransportConnectionWrapper::new(inner.clone());
332        let tasks: Vec<Task> = (0..500).map(|i| wrapper.send(measure(i as f64))).collect();
333        for task in &tasks {
334            assert!(matches!(task.wait_timeout(TIMEOUT), Some(Ok(()))));
335        }
336        let sent = inner.sent.lock().unwrap();
337        assert_eq!(sent.len(), 500);
338        for (i, message) in sent.iter().enumerate() {
339            assert_eq!(width_of(message), i as f64);
340        }
341    }
342
343    #[test]
344    fn a_failed_send_fails_its_task() {
345        let inner = FakeConnection::new(true);
346        let wrapper = TransportConnectionWrapper::new(inner);
347        match wrapper.send(measure(1.0)).wait_timeout(TIMEOUT) {
348            Some(Err(Error::InvalidOperation(message))) => assert_eq!(message, "refused"),
349            other => panic!("expected the failure of the send, got {:?}", other),
350        }
351    }
352
353    #[test]
354    fn dispose_and_start_go_to_the_inner_connection() {
355        let inner = FakeConnection::new(false);
356        let wrapper = TransportConnectionWrapper::new(inner.clone());
357        wrapper.start();
358        wrapper.dispose();
359        assert_eq!(inner.started.load(Ordering::SeqCst), 1);
360        assert_eq!(inner.disposed.load(Ordering::SeqCst), 1);
361    }
362}