add ability to set the errno value

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2023-05-02 14:31:47 +02:00
parent 2aa0c70d0d
commit 56bb74b196
5 changed files with 50 additions and 4 deletions

View File

@ -129,6 +129,7 @@ pub struct FunctionAttrs {
pub cv_variable: Option<Ident>,
pub prototype: Option<String>,
pub serialize_error: bool,
pub errno: bool,
}
impl TryFrom<AttributeArgs> for FunctionAttrs {
@ -160,6 +161,8 @@ impl TryFrom<AttributeArgs> for FunctionAttrs {
attrs.raw_return = true;
} else if path.is_ident("serialize_error") {
attrs.serialize_error = true;
} else if path.is_ident("errno") {
attrs.errno = true;
} else {
error!(path => "unknown attribute");
}

View File

@ -368,6 +368,12 @@ fn handle_return_kind(
TokenStream::new()
};
let copy_errno = if attr.errno {
quote! { ::perlmod::error::copy_errno_to_libc(); }
} else {
TokenStream::new()
};
let pthx = crate::pthx_param();
match ret.value {
ReturnValue::None => {
@ -398,7 +404,9 @@ fn handle_return_kind(
#[doc(hidden)]
#vis extern "C" fn #xs_name(#pthx #cv_arg_name: *mut ::perlmod::ffi::CV) {
unsafe {
match #impl_xs_name(#cv_arg_passed) {
let res = #impl_xs_name(#cv_arg_passed);
#copy_errno
match res {
Ok(()) => (),
Err(sv) => ::perlmod::ffi::croak(sv),
}
@ -441,7 +449,9 @@ fn handle_return_kind(
#[doc(hidden)]
#vis extern "C" fn #xs_name(#pthx #cv_arg_name: *mut ::perlmod::ffi::CV) {
unsafe {
match #impl_xs_name(#cv_arg_passed) {
let res = #impl_xs_name(#cv_arg_passed);
#copy_errno
match res {
Ok(sv) => ::perlmod::ffi::stack_push_raw(sv),
Err(sv) => ::perlmod::ffi::croak(sv),
}
@ -523,7 +533,9 @@ fn handle_return_kind(
#[doc(hidden)]
#vis extern "C" fn #xs_name(#pthx #cv_arg_name: *mut ::perlmod::ffi::CV) {
unsafe {
match #impl_xs_name(#cv_arg_passed) {
let res = #impl_xs_name(#cv_arg_passed);
#copy_errno
match res {
Ok(sv) => { #push },
Err(sv) => ::perlmod::ffi::croak(sv),
}

View File

@ -92,7 +92,7 @@ mod export {
b: String,
}
#[export(serialize_error)]
#[export(serialize_error, errno)]
fn test_deserialized_error(fail: bool) -> Result<&'static str, MyError> {
if fail {
Err(MyError {
@ -100,6 +100,7 @@ mod export {
b: "second".to_string(),
})
} else {
::perlmod::error::set_errno(77);
Ok("worked")
}
}

View File

@ -1,6 +1,8 @@
//! Error types.
use std::cell::Cell;
use std::fmt;
use std::os::raw::c_int;
/// Error returned by `TryFrom` implementations between `Scalar`, `Array` and `Hash`.
#[derive(Debug)]
@ -84,3 +86,30 @@ impl fmt::Display for MagicError {
}
impl std::error::Error for MagicError {}
thread_local! {
static ERRNO: Cell<c_int> = Cell::new(0);
}
/// Set the perlmod-specific `errno` value. This is *not* libc's `errno`, but a separate storage
/// location not touched by other C or perl functions. An `#[export(errno)]` function will copy
/// this value to libc's errno location at right before returning into the perl stack (*after* all
/// side effects such as destructors have already finished).
pub fn set_errno(value: c_int) {
ERRNO.with(|v| v.set(value))
}
/// *Not* `libc`'s `errno`, this retrieves a value previously set with [`set_errno`], see its
/// description for details.
pub fn get_errno() -> c_int {
ERRNO.with(|v| v.get())
}
/// This is part of the proc-macro API and is of little use to users of this crate directly.
/// When manually implementing "xsubs" this can be used before returning as a shortcut to copying
/// the perlmod errno value to libc.
pub unsafe fn copy_errno_to_libc() {
unsafe {
libc::__errno_location().write(get_errno());
}
}

View File

@ -122,6 +122,7 @@ my $sub = RSPM::Foo142::test_substr_return($orig);
print("[$orig] [$sub]\n");
my $ok = RSPM::Foo142::test_deserialized_error(0);
die "test_deserialized_error failed to set errno value\n" if $! != 77;
die "test_deserialized_error failed to return a value\n" if $ok ne 'worked';
$ok = eval { RSPM::Foo142::test_deserialized_error(1) };
die "test_deserialized_error error case returned a value\n" if defined $ok;