diff --git a/.github/workflows/msrv.yml b/.github/workflows/msrv.yml index 2e59e1897a..554bde540e 100644 --- a/.github/workflows/msrv.yml +++ b/.github/workflows/msrv.yml @@ -23,6 +23,10 @@ permissions: jobs: test-min-rust-version: + # PowerShell core is available on all platforms and can be used to unify scripts + defaults: + run: + shell: pwsh runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -30,4 +34,6 @@ jobs: with: crate: cargo-msrv - name: Verify minimum rust version - run: cargo-msrv --path extendr-api/ verify + run: | + . ./ci-cargo.ps1 + ci-cargo msrv --path extendr-api/ verify -ActionName "Verify Rust MSRV" diff --git a/extendr-api/src/lang_macros.rs b/extendr-api/src/lang_macros.rs index 77e6323075..ce2fe55d83 100644 --- a/extendr-api/src/lang_macros.rs +++ b/extendr-api/src/lang_macros.rs @@ -1,11 +1,10 @@ //! Argument parsing and checking. //! -use libR_sys::*; -//use crate::robj::*; use crate::robj::GetSexp; use crate::robj::Robj; use crate::single_threaded; +use libR_sys::*; /// Convert a list of tokens to an array of tuples. #[doc(hidden)] diff --git a/extendr-api/src/lib.rs b/extendr-api/src/lib.rs index 1132dcc73e..94e9f082cd 100644 --- a/extendr-api/src/lib.rs +++ b/extendr-api/src/lib.rs @@ -387,9 +387,7 @@ pub use lang_macros::*; pub use na::*; pub use rmacros::*; pub use robj::*; -pub use thread_safety::{ - catch_r_error, handle_panic, single_threaded, this_thread_id, throw_r_error, -}; +pub use thread_safety::{catch_r_error, handle_panic, single_threaded, throw_r_error}; pub use wrapper::*; pub use extendr_macros::*; diff --git a/extendr-api/src/ownership.rs b/extendr-api/src/ownership.rs index f964576db5..de3ee39dbe 100644 --- a/extendr-api/src/ownership.rs +++ b/extendr-api/src/ownership.rs @@ -4,9 +4,9 @@ //! A single preserved vector holds ownership of all protected objects. //! //! Objects are reference counted, so multiple calls are possible, -//! unlike R_PreserveObject. +//! unlike `R_PreserveObject`. //! -//! This module exports two functions, protect(sexp) and unprotect(sexp). +//! This module exports two functions, `protect(sexp)` and `unprotect(sexp)`. use once_cell::sync::Lazy; use std::collections::hash_map::{Entry, HashMap}; diff --git a/extendr-api/src/prelude.rs b/extendr-api/src/prelude.rs index 68185cd7db..e5c3238a83 100644 --- a/extendr-api/src/prelude.rs +++ b/extendr-api/src/prelude.rs @@ -53,9 +53,7 @@ pub use super::robj::{ RobjItertools, Slices, Types, }; -pub use super::thread_safety::{ - catch_r_error, handle_panic, single_threaded, this_thread_id, throw_r_error, -}; +pub use super::thread_safety::{catch_r_error, handle_panic, single_threaded, throw_r_error}; pub use super::wrapper::{ Complexes, Dataframe, Doubles, EnvIter, Environment, Expressions, ExternalPtr, FromList, diff --git a/extendr-api/src/thread_safety.rs b/extendr-api/src/thread_safety.rs index 55f7a8e8bb..fe85ca294f 100644 --- a/extendr-api/src/thread_safety.rs +++ b/extendr-api/src/thread_safety.rs @@ -6,35 +6,12 @@ use crate::*; /// It is not tied to an actual instance of R. static R_API_LOCK: parking_lot::ReentrantMutex<()> = parking_lot::ReentrantMutex::new(()); -// Get an integer 1.. for each thread that calls this. -pub fn this_thread_id() -> u32 { - THREAD_ID.with(|&v| v) -} - -/// Run a function single threaded. -/// Note: This will fail badly if the called function panics or calls RF_error. +/// Run `f` while ensuring that `f` runs in a single-threaded manner. /// -/// ``` -/// use extendr_api::prelude::*; -/// use std::sync::atomic::{AtomicU32, Ordering}; -/// static GLOBAL_THREAD_COUNT: AtomicU32 = AtomicU32::new(0); +/// This is intended for single-threaded access of the R's C-API. +/// It is possible to have nested calls of `single_threaded` without deadlocking. /// -/// let threads : Vec<_> = (0..100).map(|_| { -/// std::thread::spawn(move|| { -/// single_threaded(|| { -/// // check that we are single threaded. -/// let old_thread_count = GLOBAL_THREAD_COUNT.fetch_add(1, Ordering::AcqRel); -/// assert_eq!(old_thread_count, 0); -/// std::thread::sleep(std::time::Duration::from_millis(1)); -/// GLOBAL_THREAD_COUNT.fetch_sub(1, Ordering::AcqRel); -/// // recursive calls are ok. -/// assert_eq!(single_threaded(|| { -/// 1 -/// }), 1); -/// }) -/// }) -/// }).collect(); -/// ``` +/// Note: This will fail badly if the called function `f` panics or calls `Rf_error`. pub fn single_threaded(f: F) -> R where F: FnOnce() -> R, @@ -73,7 +50,7 @@ pub fn throw_r_error>(s: S) -> ! { }; } -/// Wrap an R function such as Rf_findFunction and convert errors and panics into results. +/// Wrap an R function such as `Rf_findFunction` and convert errors and panics into results. /// ```ignore /// use extendr_api::prelude::*; /// test! { diff --git a/extendr-macros/src/wrappers.rs b/extendr-macros/src/wrappers.rs index 9992e857c9..8dfc135273 100644 --- a/extendr-macros/src/wrappers.rs +++ b/extendr-macros/src/wrappers.rs @@ -204,7 +204,7 @@ pub fn make_function_wrappers( // included in panic. The advantage would be the panic cause could be included // in the R terminal error message and not only via std-err. // but it should be handled in a separate function and not in-lined here. - let err_string = format!("user function panicked: {}\0",#r_name_str); + let err_string = format!("user function panicked: {}",#r_name_str); // cannot use throw_r_error here for some reason. // handle_panic() exports err string differently than throw_r_error. extendr_api::handle_panic(err_string.as_str(), || panic!()); diff --git a/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md b/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md index 116e1ff2f6..175e051654 100644 --- a/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md +++ b/tests/extendrtests/tests/testthat/_snaps/macro-snapshot.md @@ -41,7 +41,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "hello_submodule"), + format_args!("user function panicked: {0}", "hello_submodule"), ); res }; @@ -184,7 +184,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "new"), + format_args!("user function panicked: {0}", "new"), ); res }; @@ -263,7 +263,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "set_a"), + format_args!("user function panicked: {0}", "set_a"), ); res }; @@ -354,7 +354,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "a"), + format_args!("user function panicked: {0}", "a"), ); res }; @@ -440,7 +440,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "me_owned"), + format_args!("user function panicked: {0}", "me_owned"), ); res }; @@ -525,7 +525,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "me_ref"), + format_args!("user function panicked: {0}", "me_ref"), ); res }; @@ -608,7 +608,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "me_mut"), + format_args!("user function panicked: {0}", "me_mut"), ); res }; @@ -691,7 +691,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "me_explicit_ref"), + format_args!("user function panicked: {0}", "me_explicit_ref"), ); res }; @@ -776,7 +776,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "me_explicit_mut"), + format_args!("user function panicked: {0}", "me_explicit_mut"), ); res }; @@ -1083,7 +1083,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "euclidean_dist"), + format_args!("user function panicked: {0}", "euclidean_dist"), ); res }; @@ -1278,7 +1278,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "type_aware_sum"), + format_args!("user function panicked: {0}", "type_aware_sum"), ); res }; @@ -1454,7 +1454,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "raw_identifier_in_fn_args", ), ); @@ -1538,7 +1538,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "r#true"), + format_args!("user function panicked: {0}", "r#true"), ); res }; @@ -1613,7 +1613,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "r#false"), + format_args!("user function panicked: {0}", "r#false"), ); res }; @@ -1787,7 +1787,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_implicit_strings", ), ); @@ -1876,7 +1876,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_implicit_doubles", ), ); @@ -1970,7 +1970,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_arg2_try_implicit_strings", ), ); @@ -2071,7 +2071,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_arg2_try_implicit_doubles", ), ); @@ -2168,7 +2168,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_unwrap_strings", ), ); @@ -2257,7 +2257,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_unwrap_doubles", ), ); @@ -2346,7 +2346,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_positive_control", ), ); @@ -2435,7 +2435,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "leak_negative_control", ), ); @@ -2685,7 +2685,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "new_usize"), + format_args!("user function panicked: {0}", "new_usize"), ); res }; @@ -2804,7 +2804,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "tst_altstring"), + format_args!("user function panicked: {0}", "tst_altstring"), ); res }; @@ -2931,7 +2931,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "tst_altinteger"), + format_args!("user function panicked: {0}", "tst_altinteger"), ); res }; @@ -3084,7 +3084,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "hello_world"), + format_args!("user function panicked: {0}", "hello_world"), ); res }; @@ -3152,7 +3152,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "do_nothing"), + format_args!("user function panicked: {0}", "do_nothing"), ); res }; @@ -3223,7 +3223,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "double_scalar"), + format_args!("user function panicked: {0}", "double_scalar"), ); res }; @@ -3303,7 +3303,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "int_scalar"), + format_args!("user function panicked: {0}", "int_scalar"), ); res }; @@ -3383,7 +3383,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "bool_scalar"), + format_args!("user function panicked: {0}", "bool_scalar"), ); res }; @@ -3463,7 +3463,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "char_scalar"), + format_args!("user function panicked: {0}", "char_scalar"), ); res }; @@ -3543,7 +3543,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "char_vec"), + format_args!("user function panicked: {0}", "char_vec"), ); res }; @@ -3623,7 +3623,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "double_vec"), + format_args!("user function panicked: {0}", "double_vec"), ); res }; @@ -3702,7 +3702,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "try_rfloat_na"), + format_args!("user function panicked: {0}", "try_rfloat_na"), ); res }; @@ -3772,7 +3772,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "try_rint_na"), + format_args!("user function panicked: {0}", "try_rint_na"), ); res }; @@ -3843,7 +3843,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "check_rfloat_na"), + format_args!("user function panicked: {0}", "check_rfloat_na"), ); res }; @@ -3923,7 +3923,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "check_rint_na"), + format_args!("user function panicked: {0}", "check_rint_na"), ); res }; @@ -4003,7 +4003,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "try_double_vec"), + format_args!("user function panicked: {0}", "try_double_vec"), ); res }; @@ -4091,7 +4091,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "get_doubles_element"), + format_args!("user function panicked: {0}", "get_doubles_element"), ); res }; @@ -4184,7 +4184,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "get_integers_element"), + format_args!("user function panicked: {0}", "get_integers_element"), ); res }; @@ -4277,7 +4277,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "get_logicals_element"), + format_args!("user function panicked: {0}", "get_logicals_element"), ); res }; @@ -4366,7 +4366,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "doubles_square"), + format_args!("user function panicked: {0}", "doubles_square"), ); res }; @@ -4450,7 +4450,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "complexes_square"), + format_args!("user function panicked: {0}", "complexes_square"), ); res }; @@ -4534,7 +4534,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "integers_square"), + format_args!("user function panicked: {0}", "integers_square"), ); res }; @@ -4618,7 +4618,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "logicals_not"), + format_args!("user function panicked: {0}", "logicals_not"), ); res }; @@ -4698,7 +4698,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "check_default"), + format_args!("user function panicked: {0}", "check_default"), ); res }; @@ -4794,7 +4794,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "special_param_names"), + format_args!("user function panicked: {0}", "special_param_names"), ); res }; @@ -4881,7 +4881,7 @@ let err_string = { let res = ::alloc::fmt::format( format_args!( - "user function panicked: {0}\0", + "user function panicked: {0}", "__00__special_function_name", ), ); @@ -4953,7 +4953,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "test.rename.rlike"), + format_args!("user function panicked: {0}", "test.rename.rlike"), ); res }; @@ -5024,7 +5024,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "get_default_value"), + format_args!("user function panicked: {0}", "get_default_value"), ); res }; @@ -5104,7 +5104,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "add_5_if_not_null"), + format_args!("user function panicked: {0}", "add_5_if_not_null"), ); res }; @@ -5232,7 +5232,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "new"), + format_args!("user function panicked: {0}", "new"), ); res }; @@ -5309,7 +5309,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "set_a"), + format_args!("user function panicked: {0}", "set_a"), ); res }; @@ -5395,7 +5395,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "a"), + format_args!("user function panicked: {0}", "a"), ); res }; @@ -5476,7 +5476,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "me"), + format_args!("user function panicked: {0}", "me"), ); res }; @@ -5559,7 +5559,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "restore_from_robj"), + format_args!("user function panicked: {0}", "restore_from_robj"), ); res }; @@ -5642,7 +5642,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "get_default_value"), + format_args!("user function panicked: {0}", "get_default_value"), ); res }; @@ -5782,7 +5782,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "new"), + format_args!("user function panicked: {0}", "new"), ); res }; @@ -5857,7 +5857,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "__name_test"), + format_args!("user function panicked: {0}", "__name_test"), ); res }; @@ -6005,7 +6005,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "new"), + format_args!("user function panicked: {0}", "new"), ); res }; @@ -6082,7 +6082,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "a"), + format_args!("user function panicked: {0}", "a"), ); res }; @@ -6214,7 +6214,7 @@ drop(unwind_err); let err_string = { let res = ::alloc::fmt::format( - format_args!("user function panicked: {0}\0", "my_device"), + format_args!("user function panicked: {0}", "my_device"), ); res };