Skip to content

Commit d7a1ce1

Browse files
authored
libprocessing ffi error handling (#1293)
* FFI error setup * Clear before init.
1 parent 45ce5e7 commit d7a1ce1

File tree

8 files changed

+5120
-216
lines changed

8 files changed

+5120
-216
lines changed

Cargo.lock

Lines changed: 4998 additions & 202 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,3 @@
1-
[package]
2-
name = "libProcessing"
3-
version = "0.1.0"
4-
edition = "2024"
5-
61
[workspace]
72
resolver = "3"
83
members = ["ffi","renderer"]

ffi/src/error.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
use std::cell::RefCell;
2+
use std::ffi::{c_char, CString};
3+
use renderer::error::ProcessingError;
4+
5+
thread_local! {
6+
static LAST_ERROR: RefCell<Option<CString>> = RefCell::new(None);
7+
}
8+
9+
/// Check if the last operation resulted in an error. Returns a pointer to an error message, or null
10+
/// if there was no error.
11+
#[unsafe(no_mangle)]
12+
pub extern "C" fn processing_check_error() -> *const c_char {
13+
LAST_ERROR.with(|last| {
14+
last.borrow()
15+
.as_ref()
16+
.map(|s| s.as_ptr())
17+
.unwrap_or(std::ptr::null())
18+
})
19+
}
20+
21+
/// Set the last error message.
22+
pub fn set_error(error_msg: &str) {
23+
LAST_ERROR.with(|last| {
24+
*last.borrow_mut() = Some(CString::new(error_msg).unwrap_or_else(|_| {
25+
CString::new("Failed to allocate error message".to_string()).unwrap()
26+
}));
27+
});
28+
}
29+
30+
/// Clear the last error message.
31+
pub fn clear_error() {
32+
LAST_ERROR.with(|last| {
33+
*last.borrow_mut() = None;
34+
});
35+
}
36+
37+
38+
/// Check the result of an operation, setting the last error if there was one.
39+
pub fn check<T>(result: Result<T, ProcessingError>) -> Option<T> {
40+
match result {
41+
Ok(value) => {
42+
Some(value)
43+
}
44+
Err(err) => {
45+
set_error(&err.to_string());
46+
None
47+
}
48+
}
49+
}

ffi/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
mod error;
2+
13
#[unsafe(no_mangle)]
2-
pub extern "C" fn processing_add(left: u64, right: u64) -> u64 {
3-
renderer::add(left, right)
4+
pub extern "C" fn processing_init() {
5+
error::clear_error();
6+
error::check(renderer::init());
47
}

renderer/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,9 @@ version = "0.1.0"
44
edition = "2024"
55

66
[dependencies]
7+
tracing = "0.1"
8+
tracing-subscriber = "0.3"
9+
bevy = { version = "0.16", no-default-features = true, features = [
10+
"bevy_render"
11+
] }
12+
thiserror = "2"

renderer/src/error.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
use thiserror::Error;
2+
3+
pub type Result<T> = std::result::Result<T, ProcessingError>;
4+
5+
6+
#[derive(Error, Debug)]
7+
pub enum ProcessingError {
8+
#[error("App was accessed from multiple threads")]
9+
AppAccess,
10+
#[error("Error initializing tracing: {0}")]
11+
Tracing(#[from] tracing::subscriber::SetGlobalDefaultError),
12+
}

renderer/src/lib.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,50 @@
1-
pub fn add(left: u64, right: u64) -> u64 {
2-
println!("Adding {} and {} in Rust", left, right);
3-
left + right
4-
}
1+
pub mod error;
2+
3+
// Once-cell for our app
4+
use crate::error::Result;
5+
use bevy::app::App;
6+
use bevy::log::tracing_subscriber;
7+
use std::sync::{Arc, Mutex, OnceLock};
8+
use tracing::{debug, info};
9+
10+
static IS_INIT: OnceLock<()> = OnceLock::new();
11+
12+
thread_local! {
13+
static APP: OnceLock<App> = OnceLock::default();
14+
}
15+
16+
/// Initialize the app, if not already initialized. Must be called from the main thread and cannot
17+
/// be called concurrently from multiple threads.
18+
pub fn init() -> Result<()> {
19+
setup_tracing()?;
20+
info!("Initializing libprocessing");
21+
22+
let is_init = IS_INIT.get().is_some();
23+
let thread_has_app = APP.with(|app_lock| app_lock.get().is_some());
24+
if is_init && !thread_has_app {
25+
return Err(error::ProcessingError::AppAccess);
26+
}
27+
if is_init && thread_has_app {
28+
debug!("App already initialized");
29+
return Ok(());
30+
}
31+
32+
APP.with(|app_lock| {
33+
app_lock.get_or_init(|| {
34+
IS_INIT.get_or_init(|| ());
35+
let mut app = App::new();
36+
37+
app.add_plugins(bevy::MinimalPlugins);
38+
39+
app
40+
});
41+
});
42+
43+
Ok(())
44+
}
45+
46+
fn setup_tracing() -> Result<()> {
47+
let subscriber = tracing_subscriber::FmtSubscriber::new();
48+
tracing::subscriber::set_global_default(subscriber)?;
49+
Ok(())
50+
}

src/main.rs

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)