modify LazyLock poison panic message
Fixes an issue where if the underlying `Once` panics because it is poisoned, the panic displays the wrong message. Signed-off-by: Connor Tsui <connor.tsui20@gmail.com>
This commit is contained in:
parent
6ba0ce4094
commit
06eb782c4e
2 changed files with 59 additions and 34 deletions
|
|
@ -244,7 +244,11 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
|||
#[inline]
|
||||
#[stable(feature = "lazy_cell", since = "1.80.0")]
|
||||
pub fn force(this: &LazyLock<T, F>) -> &T {
|
||||
this.once.call_once(|| {
|
||||
this.once.call_once_force(|state| {
|
||||
if state.is_poisoned() {
|
||||
panic_poisoned();
|
||||
}
|
||||
|
||||
// SAFETY: `call_once` only runs this closure once, ever.
|
||||
let data = unsafe { &mut *this.data.get() };
|
||||
let f = unsafe { ManuallyDrop::take(&mut data.f) };
|
||||
|
|
@ -257,8 +261,7 @@ impl<T, F: FnOnce() -> T> LazyLock<T, F> {
|
|||
// * the closure was called and initialized `value`.
|
||||
// * the closure was called and panicked, so this point is never reached.
|
||||
// * the closure was not called, but a previous call initialized `value`.
|
||||
// * the closure was not called because the Once is poisoned, so this point
|
||||
// is never reached.
|
||||
// * the closure was not called because the Once is poisoned, which we handled above.
|
||||
// So `value` has definitely been initialized and will not be modified again.
|
||||
unsafe { &*(*this.data.get()).value }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,16 +33,6 @@ fn lazy_default() {
|
|||
assert_eq!(CALLED.load(SeqCst), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn lazy_poisoning() {
|
||||
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
|
||||
for _ in 0..2 {
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
|
||||
fn sync_lazy_new() {
|
||||
|
|
@ -123,16 +113,6 @@ fn static_sync_lazy_via_fn() {
|
|||
assert_eq!(xs(), &vec![1, 2, 3]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn sync_lazy_poisoning() {
|
||||
let x: LazyLock<String> = LazyLock::new(|| panic!("kaboom"));
|
||||
for _ in 0..2 {
|
||||
let res = panic::catch_unwind(|| x.len());
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
// Check that we can infer `T` from closure's type.
|
||||
#[test]
|
||||
fn lazy_type_inference() {
|
||||
|
|
@ -145,17 +125,6 @@ fn is_sync_send() {
|
|||
assert_traits::<LazyLock<String>>();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic = "has previously been poisoned"]
|
||||
fn lazy_force_mut_panic() {
|
||||
let mut lazy = LazyLock::<String>::new(|| panic!());
|
||||
panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = LazyLock::force_mut(&mut lazy);
|
||||
}))
|
||||
.unwrap_err();
|
||||
let _ = &*lazy;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lazy_force_mut() {
|
||||
let s = "abc".to_owned();
|
||||
|
|
@ -165,3 +134,56 @@ fn lazy_force_mut() {
|
|||
p.clear();
|
||||
LazyLock::force_mut(&mut lazy);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
fn lazy_poisoning() {
|
||||
let x: LazyCell<String> = LazyCell::new(|| panic!("kaboom"));
|
||||
for _ in 0..2 {
|
||||
let res = panic::catch_unwind(panic::AssertUnwindSafe(|| x.len()));
|
||||
assert!(res.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies that when a `LazyLock` is poisoned, it panics with the correct error message ("LazyLock
|
||||
/// instance has previously been poisoned") instead of the underlying `Once` error message.
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
#[should_panic(expected = "LazyLock instance has previously been poisoned")]
|
||||
fn lazy_lock_deref_panic() {
|
||||
let lazy: LazyLock<String> = LazyLock::new(|| panic!("initialization failed"));
|
||||
|
||||
// First access will panic during initialization.
|
||||
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = &*lazy;
|
||||
}));
|
||||
|
||||
// Second access should panic with the poisoned message.
|
||||
let _ = &*lazy;
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "LazyLock instance has previously been poisoned")]
|
||||
fn lazy_lock_deref_mut_panic() {
|
||||
let mut lazy: LazyLock<String> = LazyLock::new(|| panic!("initialization failed"));
|
||||
|
||||
// First access will panic during initialization.
|
||||
let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
let _ = LazyLock::force_mut(&mut lazy);
|
||||
}));
|
||||
|
||||
// Second access should panic with the poisoned message.
|
||||
let _ = &*lazy;
|
||||
}
|
||||
|
||||
/// Verifies that when the initialization closure panics with a custom message, that message is
|
||||
/// preserved and not overridden by `LazyLock`.
|
||||
#[test]
|
||||
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
|
||||
#[should_panic(expected = "custom panic message from closure")]
|
||||
fn lazy_lock_preserves_closure_panic_message() {
|
||||
let lazy: LazyLock<String> = LazyLock::new(|| panic!("custom panic message from closure"));
|
||||
|
||||
// This should panic with the original message from the closure.
|
||||
let _ = &*lazy;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue