Skip to content

Commit

Permalink
Improve host api
Browse files Browse the repository at this point in the history
Instead of exposing the separation of a host background thead and an
associated handle, create a single host constructor to use in the public
api.
  • Loading branch information
zthompson47 authored and hannobraun committed Jan 6, 2023
1 parent 1934b63 commit 257ff37
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 40 deletions.
39 changes: 19 additions & 20 deletions crates/fj-host/src/host.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
use std::thread;
use std::thread::{self, JoinHandle};

use crossbeam_channel::{self, Receiver, Sender};
use fj_interop::processed_shape::ProcessedShape;
use fj_operations::shape_processor::ShapeProcessor;
use winit::event_loop::EventLoopProxy;

use crate::{Error, HostCommand, HostHandle, Model, Watcher};
use crate::{Error, HostCommand, Model, Watcher};

// Use a zero-sized error type to silence `#[warn(clippy::result_large_err)]`.
// The only error from `EventLoopProxy::send_event` is `EventLoopClosed<T>`,
// so we don't need the actual value. We just need to know there was an error.
/// Error type for sending on a channel with a closed event loop
pub struct EventLoopClosed;
pub(crate) struct EventLoopClosed;

/// A Fornjot model host that runs in the background
pub struct Host {
pub(crate) struct HostThread {
shape_processor: ShapeProcessor,
event_loop_proxy: EventLoopProxy<ModelEvent>,
command_tx: Sender<HostCommand>,
command_rx: Receiver<HostCommand>,
}

impl Host {
/// Create a new `Host` that will process models for an event loop.
pub fn new(
impl HostThread {
// Spawn a background thread that will process models for an event loop.
pub(crate) fn spawn(
shape_processor: ShapeProcessor,
event_loop_proxy: EventLoopProxy<ModelEvent>,
) -> Self {
) -> (Sender<HostCommand>, JoinHandle<Result<(), EventLoopClosed>>) {
let (command_tx, command_rx) = crossbeam_channel::unbounded();
Self {
let command_tx_2 = command_tx.clone();

let host_thread = Self {
shape_processor,
event_loop_proxy,
command_tx,
command_rx,
}
}
};

/// Run a background thread to watch for updates and process a model.
pub fn spawn(mut self) -> HostHandle {
let command_tx = self.command_tx.clone();
let join_handle = host_thread.spawn_thread();

let host_thread = thread::Builder::new()
(command_tx_2, join_handle)
}

fn spawn_thread(mut self) -> JoinHandle<Result<(), EventLoopClosed>> {
thread::Builder::new()
.name("host".to_string())
.spawn(move || -> Result<(), EventLoopClosed> {
let mut model: Option<Model> = None;
Expand Down Expand Up @@ -83,9 +84,7 @@ impl Host {

Ok(())
})
.expect("Cannot create OS thread for host");

HostHandle::new(command_tx, host_thread)
.expect("Cannot create OS thread for host")
}

// Evaluate and process a model.
Expand Down
24 changes: 15 additions & 9 deletions crates/fj-host/src/host_handle.rs
Original file line number Diff line number Diff line change
@@ -1,38 +1,44 @@
use std::thread::JoinHandle;

use crossbeam_channel::Sender;
use fj_operations::shape_processor::ShapeProcessor;
use winit::event_loop::EventLoopProxy;

use crate::{EventLoopClosed, Model};
use crate::{EventLoopClosed, HostThread, Model, ModelEvent};

/// A handle for sending commands to a spawned host
pub struct HostHandle {
/// A host for watching models and responding to model updates
pub struct Host {
command_tx: Sender<HostCommand>,
host_thread: Option<JoinHandle<Result<(), EventLoopClosed>>>,
model_loaded: bool,
}

impl HostHandle {
/// Create a `HostHandle` with a send channel and the host thread handle.
impl Host {
/// Create a host with a shape processor and a send channel to the event
/// loop.
pub fn new(
command_tx: Sender<HostCommand>,
host_thread: JoinHandle<Result<(), EventLoopClosed>>,
shape_processor: ShapeProcessor,
event_loop_proxy: EventLoopProxy<ModelEvent>,
) -> Self {
let (command_tx, host_thread) =
HostThread::spawn(shape_processor, event_loop_proxy);

Self {
command_tx,
host_thread: Some(host_thread),
model_loaded: false,
}
}

/// Send a model to the host for evaluation.
/// Send a model to the host for evaluation and processing.
pub fn load_model(&mut self, model: Model) {
self.command_tx
.try_send(HostCommand::LoadModel(model))
.expect("Host channel disconnected unexpectedly");
self.model_loaded = true;
}

/// Whether a model has been sent to a host for watching and evaluation
/// Whether a model has been sent to the host yet
pub fn is_model_loaded(&self) -> bool {
self.model_loaded
}
Expand Down
6 changes: 4 additions & 2 deletions crates/fj-host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ mod parameters;
mod platform;
mod watcher;

pub(crate) use self::host::{EventLoopClosed, HostThread};

pub use self::{
host::{EventLoopClosed, Host, ModelEvent},
host_handle::{HostCommand, HostHandle},
host::ModelEvent,
host_handle::{Host, HostCommand},
model::{Error, Evaluation, Model},
parameters::Parameters,
watcher::Watcher,
Expand Down
10 changes: 5 additions & 5 deletions crates/fj-window/src/event_loop_handler.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use fj_host::{HostHandle, Model, ModelEvent, Parameters};
use fj_host::{Host, Model, ModelEvent, Parameters};
use fj_operations::shape_processor;
use fj_viewer::{
GuiState, InputEvent, NormalizedScreenPosition, Screen, ScreenSize,
Expand All @@ -20,7 +20,7 @@ pub struct EventLoopHandler {
pub window: Window,
pub viewer: Viewer,
pub egui_winit_state: egui_winit::State,
pub host_handle: HostHandle,
pub host: Host,
pub status: StatusReport,
pub held_mouse_button: Option<MouseButton>,

Expand All @@ -40,7 +40,7 @@ impl EventLoopHandler {
control_flow: &mut ControlFlow,
) -> Result<(), Error> {
// Trigger a panic if the host thead has panicked.
self.host_handle.propagate_panic();
self.host.propagate_panic();

if let Event::WindowEvent { event, .. } = &event {
let egui_winit::EventResponse {
Expand Down Expand Up @@ -177,7 +177,7 @@ impl EventLoopHandler {

let gui_state = GuiState {
status: &self.status,
model_available: self.host_handle.is_model_loaded(),
model_available: self.host.is_model_loaded(),
};
let new_model_path = self.viewer.draw(
pixels_per_point,
Expand All @@ -187,7 +187,7 @@ impl EventLoopHandler {
if let Some(model_path) = new_model_path {
let model =
Model::new(model_path, Parameters::empty())?;
self.host_handle.load_model(model);
self.host.load_model(model);
}
}
}
Expand Down
7 changes: 3 additions & 4 deletions crates/fj-window/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,18 @@ pub fn run(

let egui_winit_state = egui_winit::State::new(&event_loop);

let host = Host::new(shape_processor, event_loop.create_proxy());
let mut host_handle = host.spawn();
let mut host = Host::new(shape_processor, event_loop.create_proxy());

if let Some(model) = model {
host_handle.load_model(model);
host.load_model(model);
}

let mut handler = EventLoopHandler {
invert_zoom,
window,
viewer,
egui_winit_state,
host_handle,
host,
status: StatusReport::new(),
held_mouse_button: None,
new_size: None,
Expand Down

0 comments on commit 257ff37

Please sign in to comment.