diff --git a/src/error.rs b/src/error.rs index f6e4e862..0fbec4c7 100644 --- a/src/error.rs +++ b/src/error.rs @@ -293,3 +293,38 @@ impl Error for UriError { } pub type UriValidator = Fn(&Url) -> Result<(), UriError>; + +#[derive(Debug)] +pub struct PanicError; + +impl ToGError for PanicError { + fn to_gerror(&self) -> (u32, i32) { + (gst_library_error_domain(), 1) + } +} + +macro_rules! panic_to_error( + ($wrap:expr, $ret:expr, $code:block) => {{ + if $wrap.panicked.load(Ordering::Relaxed) { + error_msg!(PanicError, ["Panicked"]).post($wrap.raw); + return $ret; + } + + let result = panic::catch_unwind(AssertUnwindSafe(|| $code)); + + match result { + Ok(result) => result, + Err(err) => { + $wrap.panicked.store(true, Ordering::Relaxed); + if let Some(cause) = err.downcast_ref::<&str>() { + error_msg!(PanicError, ["Panicked: {}", cause]).post($wrap.raw); + } else if let Some(cause) = err.downcast_ref::() { + error_msg!(PanicError, ["Panicked: {}", cause]).post($wrap.raw); + } else { + error_msg!(PanicError, ["Panicked"]).post($wrap.raw); + } + $ret + } + } + }} +); diff --git a/src/rssink.rs b/src/rssink.rs index f7bdc6fd..f3cdf5c9 100644 --- a/src/rssink.rs +++ b/src/rssink.rs @@ -22,7 +22,10 @@ use std::ffi::{CStr, CString}; use std::slice; use std::ptr; +use std::panic::{self, AssertUnwindSafe}; + use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, Ordering}; use url::Url; @@ -51,10 +54,11 @@ impl ToGError for SinkError { } pub struct SinkWrapper { - sink_raw: *mut c_void, + raw: *mut c_void, uri: Mutex<(Option, bool)>, uri_validator: Box, sink: Mutex>, + panicked: AtomicBool, } pub trait Sink { @@ -67,12 +71,13 @@ pub trait Sink { } impl SinkWrapper { - fn new(sink_raw: *mut c_void, sink: Box) -> SinkWrapper { + fn new(raw: *mut c_void, sink: Box) -> SinkWrapper { SinkWrapper { - sink_raw: sink_raw, + raw: raw, uri: Mutex::new((None, false)), uri_validator: sink.uri_validator(), sink: Mutex::new(sink), + panicked: AtomicBool::new(false), } } } @@ -95,96 +100,107 @@ pub unsafe extern "C" fn sink_set_uri(ptr: *const SinkWrapper, cerr: *mut c_void) -> GBoolean { let wrap: &SinkWrapper = &*ptr; - let uri_storage = &mut wrap.uri.lock().unwrap(); - if uri_storage.1 { - UriError::new(UriErrorKind::BadState, Some("Already started".to_string())) - .into_gerror(cerr); - return GBoolean::False; - } + panic_to_error!(wrap, GBoolean::False, { + let uri_storage = &mut wrap.uri.lock().unwrap(); - uri_storage.0 = None; - if uri_ptr.is_null() { - GBoolean::True - } else { - let uri_str = CStr::from_ptr(uri_ptr).to_str().unwrap(); + if uri_storage.1 { + UriError::new(UriErrorKind::BadState, Some("Already started".to_string())) + .into_gerror(cerr); + return GBoolean::False; + } - match Url::parse(uri_str) { - Ok(uri) => { - if let Err(err) = (*wrap.uri_validator)(&uri) { - err.into_gerror(cerr); + uri_storage.0 = None; + if uri_ptr.is_null() { + GBoolean::True + } else { + let uri_str = CStr::from_ptr(uri_ptr).to_str().unwrap(); + + match Url::parse(uri_str) { + Ok(uri) => { + if let Err(err) = (*wrap.uri_validator)(&uri) { + err.into_gerror(cerr); + + GBoolean::False + } else { + uri_storage.0 = Some(uri); + + GBoolean::True + } + } + Err(err) => { + UriError::new(UriErrorKind::BadUri, + Some(format!("Failed to parse URI '{}': {}", uri_str, err))) + .into_gerror(cerr); GBoolean::False - } else { - uri_storage.0 = Some(uri); - - GBoolean::True } } - Err(err) => { - UriError::new(UriErrorKind::BadUri, - Some(format!("Failed to parse URI '{}': {}", uri_str, err))) - .into_gerror(cerr); - - GBoolean::False - } } - } + }) } #[no_mangle] pub unsafe extern "C" fn sink_get_uri(ptr: *const SinkWrapper) -> *mut c_char { let wrap: &SinkWrapper = &*ptr; - let uri_storage = &mut wrap.uri.lock().unwrap(); - match uri_storage.0 { - Some(ref uri) => CString::new(uri.as_ref().as_bytes()).unwrap().into_raw(), - None => ptr::null_mut(), - } + panic_to_error!(wrap, ptr::null_mut(), { + let uri_storage = &mut wrap.uri.lock().unwrap(); + + match uri_storage.0 { + Some(ref uri) => CString::new(uri.as_ref().as_bytes()).unwrap().into_raw(), + None => ptr::null_mut(), + } + }) } #[no_mangle] pub unsafe extern "C" fn sink_start(ptr: *const SinkWrapper) -> GBoolean { let wrap: &SinkWrapper = &*ptr; - let sink = &mut wrap.sink.lock().unwrap(); - let uri = match *wrap.uri.lock().unwrap() { - (Some(ref uri), ref mut started) => { - *started = true; + panic_to_error!(wrap, GBoolean::False, { + let sink = &mut wrap.sink.lock().unwrap(); - uri.clone() - } - (None, _) => { - error_msg!(SinkError::OpenFailed, ["No URI given"]).post(wrap.sink_raw); - return GBoolean::False; - } - }; + let uri = match *wrap.uri.lock().unwrap() { + (Some(ref uri), ref mut started) => { + *started = true; - match sink.start(uri) { - Ok(..) => GBoolean::True, - Err(ref msg) => { - wrap.uri.lock().unwrap().1 = false; - msg.post(wrap.sink_raw); - GBoolean::False + uri.clone() + } + (None, _) => { + error_msg!(SinkError::OpenFailed, ["No URI given"]).post(wrap.raw); + return GBoolean::False; + } + }; + + match sink.start(uri) { + Ok(..) => GBoolean::True, + Err(ref msg) => { + wrap.uri.lock().unwrap().1 = false; + msg.post(wrap.raw); + GBoolean::False + } } - } + }) } #[no_mangle] pub unsafe extern "C" fn sink_stop(ptr: *const SinkWrapper) -> GBoolean { let wrap: &SinkWrapper = &*ptr; - let sink = &mut wrap.sink.lock().unwrap(); + panic_to_error!(wrap, GBoolean::False, { + let sink = &mut wrap.sink.lock().unwrap(); - match sink.stop() { - Ok(..) => { - wrap.uri.lock().unwrap().1 = false; - GBoolean::True + match sink.stop() { + Ok(..) => { + wrap.uri.lock().unwrap().1 = false; + GBoolean::True + } + Err(ref msg) => { + msg.post(wrap.raw); + GBoolean::False + } } - Err(ref msg) => { - msg.post(wrap.sink_raw); - GBoolean::False - } - } + }) } #[no_mangle] @@ -193,18 +209,20 @@ pub unsafe extern "C" fn sink_render(ptr: *const SinkWrapper, data_len: usize) -> GstFlowReturn { let wrap: &SinkWrapper = &*ptr; - let sink = &mut wrap.sink.lock().unwrap(); - let data = slice::from_raw_parts(data_ptr, data_len); + panic_to_error!(wrap, GstFlowReturn::Error, { + let sink = &mut wrap.sink.lock().unwrap(); + let data = slice::from_raw_parts(data_ptr, data_len); - match sink.render(data) { - Ok(..) => GstFlowReturn::Ok, - Err(flow_error) => { - match flow_error { - FlowError::NotNegotiated(ref msg) | - FlowError::Error(ref msg) => msg.post(wrap.sink_raw), - _ => (), + match sink.render(data) { + Ok(..) => GstFlowReturn::Ok, + Err(flow_error) => { + match flow_error { + FlowError::NotNegotiated(ref msg) | + FlowError::Error(ref msg) => msg.post(wrap.raw), + _ => (), + } + flow_error.to_native() } - flow_error.to_native() } - } + }) } diff --git a/src/rssource.rs b/src/rssource.rs index 2e1d05d3..6e2dda33 100644 --- a/src/rssource.rs +++ b/src/rssource.rs @@ -22,7 +22,10 @@ use std::slice; use std::ptr; use std::u64; +use std::panic::{self, AssertUnwindSafe}; + use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, Ordering}; use url::Url; @@ -51,10 +54,11 @@ impl ToGError for SourceError { } pub struct SourceWrapper { - source_raw: *mut c_void, + raw: *mut c_void, uri: Mutex<(Option, bool)>, uri_validator: Box, source: Mutex>, + panicked: AtomicBool, } pub trait Source { @@ -70,12 +74,13 @@ pub trait Source { } impl SourceWrapper { - fn new(source_raw: *mut c_void, source: Box) -> SourceWrapper { + fn new(raw: *mut c_void, source: Box) -> SourceWrapper { SourceWrapper { - source_raw: source_raw, + raw: raw, uri: Mutex::new((None, false)), uri_validator: source.uri_validator(), source: Mutex::new(source), + panicked: AtomicBool::new(false), } } } @@ -98,115 +103,132 @@ pub unsafe extern "C" fn source_set_uri(ptr: *const SourceWrapper, cerr: *mut c_void) -> GBoolean { let wrap: &SourceWrapper = &*ptr; - let uri_storage = &mut wrap.uri.lock().unwrap(); - if uri_storage.1 { - UriError::new(UriErrorKind::BadState, Some("Already started".to_string())) - .into_gerror(cerr); - return GBoolean::False; - } + panic_to_error!(wrap, GBoolean::False, { + let uri_storage = &mut wrap.uri.lock().unwrap(); - uri_storage.0 = None; - if uri_ptr.is_null() { - GBoolean::True - } else { - let uri_str = CStr::from_ptr(uri_ptr).to_str().unwrap(); + if uri_storage.1 { + UriError::new(UriErrorKind::BadState, Some("Already started".to_string())) + .into_gerror(cerr); + return GBoolean::False; + } - match Url::parse(uri_str) { - Ok(uri) => { - if let Err(err) = (*wrap.uri_validator)(&uri) { - err.into_gerror(cerr); + uri_storage.0 = None; + if uri_ptr.is_null() { + GBoolean::True + } else { + let uri_str = CStr::from_ptr(uri_ptr).to_str().unwrap(); + + match Url::parse(uri_str) { + Ok(uri) => { + if let Err(err) = (*wrap.uri_validator)(&uri) { + err.into_gerror(cerr); + + GBoolean::False + } else { + uri_storage.0 = Some(uri); + + GBoolean::True + } + } + Err(err) => { + UriError::new(UriErrorKind::BadUri, + Some(format!("Failed to parse URI '{}': {}", uri_str, err))) + .into_gerror(cerr); GBoolean::False - } else { - uri_storage.0 = Some(uri); - - GBoolean::True } } - Err(err) => { - UriError::new(UriErrorKind::BadUri, - Some(format!("Failed to parse URI '{}': {}", uri_str, err))) - .into_gerror(cerr); - - GBoolean::False - } } - } + }) } #[no_mangle] pub unsafe extern "C" fn source_get_uri(ptr: *const SourceWrapper) -> *mut c_char { let wrap: &SourceWrapper = &*ptr; - let uri_storage = &mut wrap.uri.lock().unwrap(); + panic_to_error!(wrap, ptr::null_mut(), { + let uri_storage = &mut wrap.uri.lock().unwrap(); - match uri_storage.0 { - Some(ref uri) => CString::new(uri.as_ref().as_bytes()).unwrap().into_raw(), - None => ptr::null_mut(), - } + match uri_storage.0 { + Some(ref uri) => CString::new(uri.as_ref().as_bytes()).unwrap().into_raw(), + None => ptr::null_mut(), + } + }) } #[no_mangle] pub unsafe extern "C" fn source_is_seekable(ptr: *const SourceWrapper) -> GBoolean { let wrap: &SourceWrapper = &*ptr; - let source = &wrap.source.lock().unwrap(); - GBoolean::from_bool(source.is_seekable()) + panic_to_error!(wrap, GBoolean::False, { + let source = &wrap.source.lock().unwrap(); + + GBoolean::from_bool(source.is_seekable()) + }) } #[no_mangle] pub unsafe extern "C" fn source_get_size(ptr: *const SourceWrapper) -> u64 { let wrap: &SourceWrapper = &*ptr; - let source = &wrap.source.lock().unwrap(); + panic_to_error!(wrap, u64::MAX, { - match source.get_size() { - Some(size) => size, - None => u64::MAX, - } + let source = &wrap.source.lock().unwrap(); + + match source.get_size() { + Some(size) => size, + None => u64::MAX, + } + }) } #[no_mangle] pub unsafe extern "C" fn source_start(ptr: *const SourceWrapper) -> GBoolean { let wrap: &SourceWrapper = &*ptr; - let source = &mut wrap.source.lock().unwrap(); - let uri = match *wrap.uri.lock().unwrap() { - (Some(ref uri), ref mut started) => { - *started = true; + panic_to_error!(wrap, GBoolean::False, { + let source = &mut wrap.source.lock().unwrap(); - uri.clone() - } - (None, _) => { - error_msg!(SourceError::OpenFailed, ["No URI given"]).post(wrap.source_raw); - return GBoolean::False; - } - }; + let uri = match *wrap.uri.lock().unwrap() { + (Some(ref uri), ref mut started) => { + *started = true; - match source.start(uri) { - Ok(..) => GBoolean::True, - Err(ref msg) => { - wrap.uri.lock().unwrap().1 = false; - msg.post(wrap.source_raw); - GBoolean::False + uri.clone() + } + (None, _) => { + error_msg!(SourceError::OpenFailed, ["No URI given"]).post(wrap.raw); + return GBoolean::False; + } + }; + + match source.start(uri) { + Ok(..) => GBoolean::True, + Err(ref msg) => { + wrap.uri.lock().unwrap().1 = false; + msg.post(wrap.raw); + GBoolean::False + } } - } + }) } #[no_mangle] pub unsafe extern "C" fn source_stop(ptr: *const SourceWrapper) -> GBoolean { let wrap: &SourceWrapper = &*ptr; - let source = &mut wrap.source.lock().unwrap(); - match source.stop() { - Ok(..) => { - wrap.uri.lock().unwrap().1 = false; - GBoolean::True + panic_to_error!(wrap, GBoolean::False, { + let source = &mut wrap.source.lock().unwrap(); + + match source.stop() { + Ok(..) => { + wrap.uri.lock().unwrap().1 = false; + GBoolean::True + } + Err(ref msg) => { + msg.post(wrap.raw); + GBoolean::False + } } - Err(ref msg) => { - msg.post(wrap.source_raw); - GBoolean::False - } - } + }) } #[no_mangle] @@ -216,36 +238,42 @@ pub unsafe extern "C" fn source_fill(ptr: *const SourceWrapper, data_len_ptr: *mut usize) -> GstFlowReturn { let wrap: &SourceWrapper = &*ptr; - let source = &mut wrap.source.lock().unwrap(); - let mut data_len: &mut usize = &mut *data_len_ptr; - let mut data = slice::from_raw_parts_mut(data_ptr, *data_len); - match source.fill(offset, data) { - Ok(actual_len) => { - *data_len = actual_len; - GstFlowReturn::Ok - } - Err(flow_error) => { - match flow_error { - FlowError::NotNegotiated(ref msg) | - FlowError::Error(ref msg) => msg.post(wrap.source_raw), - _ => (), + panic_to_error!(wrap, GstFlowReturn::Error, { + let source = &mut wrap.source.lock().unwrap(); + let mut data_len: &mut usize = &mut *data_len_ptr; + let mut data = slice::from_raw_parts_mut(data_ptr, *data_len); + + match source.fill(offset, data) { + Ok(actual_len) => { + *data_len = actual_len; + GstFlowReturn::Ok + } + Err(flow_error) => { + match flow_error { + FlowError::NotNegotiated(ref msg) | + FlowError::Error(ref msg) => msg.post(wrap.raw), + _ => (), + } + flow_error.to_native() } - flow_error.to_native() } - } + }) } #[no_mangle] pub unsafe extern "C" fn source_seek(ptr: *const SourceWrapper, start: u64, stop: u64) -> GBoolean { let wrap: &SourceWrapper = &*ptr; - let source = &mut wrap.source.lock().unwrap(); - match source.seek(start, if stop == u64::MAX { None } else { Some(stop) }) { - Ok(..) => GBoolean::True, - Err(ref msg) => { - msg.post(wrap.source_raw); - GBoolean::False + panic_to_error!(wrap, GBoolean::False, { + let source = &mut wrap.source.lock().unwrap(); + + match source.seek(start, if stop == u64::MAX { None } else { Some(stop) }) { + Ok(..) => GBoolean::True, + Err(ref msg) => { + msg.post(wrap.raw); + GBoolean::False + } } - } + }) }