mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-02-20 22:56:20 +00:00
Never let panics propagate to C and instead convert them to error messages
This commit is contained in:
parent
95dd469fbf
commit
f9adac5f7e
3 changed files with 247 additions and 166 deletions
35
src/error.rs
35
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::<String>() {
|
||||
error_msg!(PanicError, ["Panicked: {}", cause]).post($wrap.raw);
|
||||
} else {
|
||||
error_msg!(PanicError, ["Panicked"]).post($wrap.raw);
|
||||
}
|
||||
$ret
|
||||
}
|
||||
}
|
||||
}}
|
||||
);
|
||||
|
|
166
src/rssink.rs
166
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<Url>, bool)>,
|
||||
uri_validator: Box<UriValidator>,
|
||||
sink: Mutex<Box<Sink>>,
|
||||
panicked: AtomicBool,
|
||||
}
|
||||
|
||||
pub trait Sink {
|
||||
|
@ -67,12 +71,13 @@ pub trait Sink {
|
|||
}
|
||||
|
||||
impl SinkWrapper {
|
||||
fn new(sink_raw: *mut c_void, sink: Box<Sink>) -> SinkWrapper {
|
||||
fn new(raw: *mut c_void, sink: Box<Sink>) -> 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()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
212
src/rssource.rs
212
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<Url>, bool)>,
|
||||
uri_validator: Box<UriValidator>,
|
||||
source: Mutex<Box<Source>>,
|
||||
panicked: AtomicBool,
|
||||
}
|
||||
|
||||
pub trait Source {
|
||||
|
@ -70,12 +74,13 @@ pub trait Source {
|
|||
}
|
||||
|
||||
impl SourceWrapper {
|
||||
fn new(source_raw: *mut c_void, source: Box<Source>) -> SourceWrapper {
|
||||
fn new(raw: *mut c_void, source: Box<Source>) -> 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
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue