fj_host/host.rs
1use std::thread::JoinHandle;
2
3use crossbeam_channel::Sender;
4use fj_operations::shape_processor::ShapeProcessor;
5
6use crate::{EventLoopClosed, HostThread, Model, ModelEvent};
7
8/// A host for watching models and responding to model updates
9pub struct Host {
10 command_tx: Sender<HostCommand>,
11 host_thread: Option<JoinHandle<Result<(), EventLoopClosed>>>,
12 model_loaded: bool,
13}
14
15impl Host {
16 /// Create a host with a shape processor and a send channel to the event
17 /// loop.
18 pub fn new(
19 shape_processor: ShapeProcessor,
20 model_event_tx: Sender<ModelEvent>,
21 ) -> Self {
22 let (command_tx, host_thread) =
23 HostThread::spawn(shape_processor, model_event_tx);
24
25 Self {
26 command_tx,
27 host_thread: Some(host_thread),
28 model_loaded: false,
29 }
30 }
31
32 /// Send a model to the host for evaluation and processing.
33 pub fn load_model(&mut self, model: Model) {
34 self.command_tx
35 .try_send(HostCommand::LoadModel(model))
36 .expect("Host channel disconnected unexpectedly");
37 self.model_loaded = true;
38 }
39
40 /// Whether a model has been sent to the host yet
41 pub fn is_model_loaded(&self) -> bool {
42 self.model_loaded
43 }
44
45 /// Check if the host thread has exited with a panic. This method runs at
46 /// each tick of the event loop. Without an explicit check, an operation
47 /// will appear to hang forever (e.g. processing a model). An error
48 /// will be printed to the terminal, but the gui will not notice until
49 /// a new `HostCommand` is issued on the disconnected channel.
50 ///
51 /// # Panics
52 ///
53 /// This method panics on purpose so the main thread can exit on an
54 /// unrecoverable error.
55 pub fn propagate_panic(&mut self) {
56 if self.host_thread.is_none() {
57 unreachable!("Constructor requires host thread")
58 }
59 if let Some(host_thread) = &self.host_thread {
60 // The host thread should not finish while this handle holds the
61 // `command_tx` channel open, so an exit means the thread panicked.
62 if host_thread.is_finished() {
63 let host_thread = self.host_thread.take().unwrap();
64 match host_thread.join() {
65 Ok(_) => {
66 unreachable!(
67 "Host thread cannot exit until host handle disconnects"
68 )
69 }
70 // The error value has already been reported by the panic
71 // in the host thread, so just ignore it here.
72 Err(_) => {
73 panic!("Host thread panicked")
74 }
75 }
76 }
77 }
78 }
79}
80
81/// Commands that can be sent to a host
82pub enum HostCommand {
83 /// Load a model to be evaluated and processed
84 LoadModel(Model),
85 /// Used by a `Watcher` to trigger evaluation when a model is edited
86 TriggerEvaluation,
87}