From a50e8f194a50344e21db06444b00e7a8f22e2477 Mon Sep 17 00:00:00 2001 From: itsmeow Date: Tue, 26 Dec 2023 20:46:30 -0500 Subject: [PATCH] Cleanup panic unwind --- src/byond.rs | 25 ++++++++++++++ src/error.rs | 2 ++ src/iconforge.rs | 87 +++++++++++------------------------------------- 3 files changed, 47 insertions(+), 67 deletions(-) diff --git a/src/byond.rs b/src/byond.rs index 9412c101..ec04ce81 100644 --- a/src/byond.rs +++ b/src/byond.rs @@ -1,3 +1,4 @@ +use crate::error::Error; use std::{ backtrace::Backtrace, borrow::Cow, @@ -116,3 +117,27 @@ pub fn set_panic_hook() { })) }); } + +/// Utility for BYOND functions to catch panic unwinds safely and return a Result, as expected. +/// Usage: catch_panic(|| internal_safe_function(arguments)) +pub fn catch_panic(f: F) -> Result +where + F: FnOnce() -> Result + std::panic::UnwindSafe, +{ + match std::panic::catch_unwind(|| f()) { + Ok(o) => o, + Err(e) => { + let message: Option = e + .downcast_ref::<&'static str>() + .map(|payload| payload.to_string()) + .or_else(|| e.downcast_ref::().cloned()); + Err(Error::Panic( + message + .unwrap_or(String::from( + "Failed to stringify panic! Check rustg-panic.log!", + )) + .to_owned(), + )) + } + } +} diff --git a/src/error.rs b/src/error.rs index 6f8cd736..6c3dbc5d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -64,6 +64,8 @@ pub enum Error { #[cfg(feature = "iconforge")] #[error("IconForge error: {0}")] IconForge(String), + #[error("Panic during function execution: {0}")] + Panic(String), } impl From for Error { diff --git a/src/iconforge.rs b/src/iconforge.rs index d8bd6385..74ee0e4f 100644 --- a/src/iconforge.rs +++ b/src/iconforge.rs @@ -1,9 +1,10 @@ // DMI spritesheet generator // Developed by itsmeow -use crate::jobs; use crate::{ + byond::catch_panic, error::Error, hash::{file_hash, string_hash}, + jobs, }; use dashmap::DashMap; use dmi::{ @@ -26,7 +27,7 @@ use std::{ use tracy_full::{frame, zone}; use twox_hash::XxHash64; type SpriteJsonMap = HashMap, BuildHasherDefault>; -/// This is used to save time decoding 'sprites' between the cache step and the generate step. +/// This is used to save time decoding 'sprites' a second time between the cache step and the generate step. static SPRITES_TO_JSON: Lazy>> = Lazy::new(|| { Arc::new(Mutex::new(HashMap::with_hasher(BuildHasherDefault::< XxHash64, @@ -44,23 +45,26 @@ byond_fn!(fn iconforge_generate(file_path, spritesheet_name, sprites, hash_icons let spritesheet_name = spritesheet_name.to_owned(); let sprites = sprites.to_owned(); let hash_icons = hash_icons.to_owned(); - Some(match generate_spritesheet_safe(&file_path, &spritesheet_name, &sprites, &hash_icons) { + let result = Some(match catch_panic(|| generate_spritesheet(&file_path, &spritesheet_name, &sprites, &hash_icons)) { Ok(o) => o.to_string(), Err(e) => e.to_string() - }) + }); + frame!(); + result }); byond_fn!(fn iconforge_generate_async(file_path, spritesheet_name, sprites, hash_icons) { - // Take ownership before passing let file_path = file_path.to_owned(); let spritesheet_name = spritesheet_name.to_owned(); let sprites = sprites.to_owned(); let hash_icons = hash_icons.to_owned(); Some(jobs::start(move || { - match generate_spritesheet_safe(&file_path, &spritesheet_name, &sprites, &hash_icons) { + let result = match catch_panic(|| generate_spritesheet(&file_path, &spritesheet_name, &sprites, &hash_icons)) { Ok(o) => o.to_string(), Err(e) => e.to_string() - } + }; + frame!(); + result })) }); @@ -80,22 +84,26 @@ byond_fn!(fn iconforge_cache_valid(input_hash, dmi_hashes, sprites) { let input_hash = input_hash.to_owned(); let dmi_hashes = dmi_hashes.to_owned(); let sprites = sprites.to_owned(); - Some(match cache_valid_safe(&input_hash, &dmi_hashes, &sprites) { + let result = Some(match catch_panic(|| cache_valid(&input_hash, &dmi_hashes, &sprites)) { Ok(o) => o.to_string(), Err(e) => e.to_string() - }) + }); + frame!(); + result }); byond_fn!(fn iconforge_cache_valid_async(input_hash, dmi_hashes, sprites) { let input_hash = input_hash.to_owned(); let dmi_hashes = dmi_hashes.to_owned(); let sprites = sprites.to_owned(); - Some(jobs::start(move || { - match cache_valid_safe(&input_hash, &dmi_hashes, &sprites) { + let result = Some(jobs::start(move || { + match catch_panic(|| cache_valid(&input_hash, &dmi_hashes, &sprites)) { Ok(o) => o.to_string(), Err(e) => e.to_string() } - })) + })); + frame!(); + result }); #[derive(Serialize)] @@ -188,33 +196,6 @@ enum Transform { Crop { x1: i32, y1: i32, x2: i32, y2: i32 }, } -fn cache_valid_safe( - input_hash: &str, - dmi_hashes: &str, - sprites: &str, -) -> std::result::Result { - match std::panic::catch_unwind(|| { - let result = cache_valid(input_hash, dmi_hashes, sprites); - frame!(); - result - }) { - Ok(o) => o, - Err(e) => { - let message: Option = e - .downcast_ref::<&'static str>() - .map(|payload| payload.to_string()) - .or_else(|| e.downcast_ref::().cloned()); - Err(Error::IconForge( - message - .unwrap_or(String::from( - "Failed to stringify panic! Check rustg-panic.log", - )) - .to_owned(), - )) - } - } -} - #[derive(Serialize)] struct CacheResult { result: String, @@ -324,34 +305,6 @@ fn cache_valid(input_hash: &str, dmi_hashes_in: &str, sprites_in: &str) -> Resul })?) } -fn generate_spritesheet_safe( - file_path: &str, - spritesheet_name: &str, - sprites: &str, - hash_icons: &str, -) -> std::result::Result { - match std::panic::catch_unwind(|| { - let result = generate_spritesheet(file_path, spritesheet_name, sprites, hash_icons); - frame!(); - result - }) { - Ok(o) => o, - Err(e) => { - let message: Option = e - .downcast_ref::<&'static str>() - .map(|payload| payload.to_string()) - .or_else(|| e.downcast_ref::().cloned()); - Err(Error::IconForge( - message - .unwrap_or(String::from( - "Failed to stringify panic! Check rustg-panic.log", - )) - .to_owned(), - )) - } - } -} - fn generate_spritesheet( file_path: &str, spritesheet_name: &str,