Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialization based models #1735

Closed
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions crates/fj-host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ libloading = "0.7.4"
notify = "5.1.0"
thiserror = "1.0.40"
tracing = "0.1.37"

[dependencies.postcard]
version = "1.0.4"
features = [ "alloc" ]
117 changes: 65 additions & 52 deletions crates/fj-host/src/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,14 @@ use std::{
io,
path::{Path, PathBuf},
process::Command,
ptr::NonNull,
str,
};

use fj::{abi, version::Version};
use fj::{
abi::{self, SelfSerializing},
version::Version,
};
use fj_operations::shape_processor;
use tracing::debug;

Expand Down Expand Up @@ -146,22 +150,55 @@ impl Model {
Some(format!("{}", Error::VersionMismatch { host, model }));
}

let init: libloading::Symbol<abi::InitFunction> = lib
.get(abi::INIT_FUNCTION_NAME.as_bytes())
let construct: libloading::Symbol<abi::ConstructModelFunction> =
lib.get(abi::CONSTRUCT_FUNCTION_NAME.as_bytes())
.map_err(Error::LoadingInit)?;

let free: libloading::Symbol<abi::FreeModelFunction> = lib
.get(abi::FREE_FUNCTION_NAME.as_bytes())
.map_err(Error::LoadingInit)?;

let mut host = Host::new(&self.parameters);
let parameters = self
.parameters
.0
.serialize()
.map_err(Error::ParameterSerialization)?;

match init(&mut abi::Host::from(&mut host)) {
abi::ffi_safe::Result::Ok(_metadata) => {}
abi::ffi_safe::Result::Err(e) => {
return Err(Error::InitializeModel(e.into()));
}
}
let parameter_length = parameters.len();
let parameters_ptr = parameters.as_ptr();

let model = host.take_model().ok_or(Error::NoModelRegistered)?;
let mut shape_data: *mut u8 = std::ptr::null_mut();
let shape_length = construct(
&mut shape_data as *mut _,
parameters_ptr,
parameter_length,
);

model.shape(&host).map_err(Error::Shape)?
let model_result = {
let shape_data =
NonNull::new(shape_data).ok_or(Error::NoModel)?;
let shape_payload = std::slice::from_raw_parts(
shape_data.as_ptr(),
shape_length,
);

fj::abi::ModelResult::deserialize(shape_payload)
};

// Free the payload before we check for an error.
free(shape_data);

let model_result =
model_result.map_err(Error::ModelDeserialization)?;
match model_result {
abi::ModelResult::Panic(panic) => {
return Err(Error::Shape(panic))
}
abi::ModelResult::Error(error) => {
return Err(Error::Shape(error))
}
abi::ModelResult::Ok(model) => model.shape,
}
};

Ok(Evaluation {
Expand All @@ -187,36 +224,6 @@ pub struct Evaluation {
pub warning: Option<String>,
}

pub struct Host<'a> {
args: &'a Parameters,
model: Option<Box<dyn fj::models::Model>>,
}

impl<'a> Host<'a> {
pub fn new(parameters: &'a Parameters) -> Self {
Self {
args: parameters,
model: None,
}
}

pub fn take_model(&mut self) -> Option<Box<dyn fj::models::Model>> {
self.model.take()
}
}

impl<'a> fj::models::Host for Host<'a> {
fn register_boxed_model(&mut self, model: Box<dyn fj::models::Model>) {
self.model = Some(model);
}
}

impl<'a> fj::models::Context for Host<'a> {
fn get_argument(&self, name: &str) -> Option<&str> {
self.args.get(name).map(String::as_str)
}
}

fn package_associated_with_directory<'m>(
metadata: &'m cargo_metadata::Metadata,
dir: &Path,
Expand Down Expand Up @@ -308,18 +315,9 @@ pub enum Error {
#[error("I/O error while loading model")]
Io(#[from] io::Error),

/// Initializing a model failed.
#[error("Unable to initialize the model")]
InitializeModel(#[source] fj::models::Error),

/// The user forgot to register a model when calling
/// [`fj::register_model!()`].
#[error("No model was registered")]
NoModelRegistered,

/// An error was returned from [`fj::models::Model::shape()`].
#[error("Unable to determine the model's geometry")]
Shape(#[source] fj::models::Error),
Shape(String),

/// An error was returned from
/// [`fj_operations::shape_processor::ShapeProcessor::process()`].
Expand Down Expand Up @@ -351,4 +349,19 @@ pub enum Error {
/// workspace root.
possible_paths: Vec<PathBuf>,
},

/// This should never happen but we need a fail safe.
/// Something about our parameter hash map couldn't be serialized.
#[error("Failed to serialize parameters for model")]
ParameterSerialization(postcard::Error),

/// This error actually is very possible.
/// If the client serialized its model incorrectly.
#[error("Failed to deserialize model from client")]
ModelDeserialization(postcard::Error),

/// The model function gave us a null pointer. There's
/// literally nothing for us to work with here.
#[error("Client returned nothing")]
NoModel,
}
Loading