-
Notifications
You must be signed in to change notification settings - Fork 163
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add feature handle-panics
#525
Merged
matthew-russo
merged 6 commits into
proptest-rs:main
from
target-san:feature/handle-panics
Dec 9, 2024
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
53a048d
Add 'handle-panics' feature
target-san 639f686
Add conditional scoped panic hook implementation
target-san 343d74f
Silence out panic from test case
target-san b438970
Updated changelog
target-san 6081fe8
Rename internal module to `internal`
target-san eb7fdfc
Licensing header
target-san File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
//- | ||
// Copyright 2024 The proptest developers | ||
// | ||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | ||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | ||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | ||
// option. This file may not be copied, modified, or distributed | ||
// except according to those terms. | ||
|
||
#[cfg(feature = "handle-panics")] | ||
mod internal { | ||
//! Implementation of scoped panic hooks | ||
//! | ||
//! 1. `with_hook` serves as entry point, it executes body closure with panic hook closure | ||
//! installed as scoped panic hook | ||
//! 2. Upon first execution, current panic hook is replaced with `scoped_hook_dispatcher` | ||
//! in a thread-safe manner, and original hook is stored for later use | ||
//! 3. When panic occurs, `scoped_hook_dispatcher` either delegates execution to scoped | ||
//! panic hook, if one is installed, or back to original hook stored earlier. | ||
//! This preserves original behavior when scoped hook isn't used | ||
//! 4. When `with_hook` is used, it replaces stored scoped hook pointer with pointer to | ||
//! hook closure passed as parameter. Old hook pointer is set to be restored unconditionally | ||
//! via drop guard. Then, normal body closure is executed. | ||
use std::boxed::Box; | ||
use std::cell::Cell; | ||
use std::panic::{set_hook, take_hook, PanicInfo}; | ||
use std::sync::Once; | ||
use std::{mem, ptr}; | ||
|
||
thread_local! { | ||
/// Pointer to currently installed scoped panic hook, if any | ||
/// | ||
/// NB: pointers to arbitrary fn's are fat, and Rust doesn't allow crafting null pointers | ||
/// to fat objects. So we just store const pointer to tuple with whatever data we need | ||
static SCOPED_HOOK_PTR: Cell<*const (*mut dyn FnMut(&PanicInfo<'_>),)> = Cell::new(ptr::null()); | ||
} | ||
|
||
static INIT_ONCE: Once = Once::new(); | ||
/// Default panic hook, the one which was present before installing scoped one | ||
/// | ||
/// NB: no need for external sync, value is mutated only once, when init is performed | ||
static mut DEFAULT_HOOK: Option<Box<dyn Fn(&PanicInfo<'_>) + Send + Sync>> = | ||
None; | ||
/// Replaces currently installed panic hook with `scoped_hook_dispatcher` once, | ||
/// in a thread-safe manner | ||
fn init() { | ||
INIT_ONCE.call_once(|| { | ||
let old_handler = take_hook(); | ||
set_hook(Box::new(scoped_hook_dispatcher)); | ||
unsafe { | ||
DEFAULT_HOOK = Some(old_handler); | ||
} | ||
}); | ||
} | ||
/// Panic hook which delegates execution to scoped hook, | ||
/// if one installed, or to default hook | ||
fn scoped_hook_dispatcher(info: &PanicInfo<'_>) { | ||
let handler = SCOPED_HOOK_PTR.get(); | ||
if !handler.is_null() { | ||
// It's assumed that if container's ptr is not null, ptr to `FnMut` is non-null too. | ||
// Correctness **must** be ensured by hook switch code in `with_hook` | ||
let hook = unsafe { &mut *(*handler).0 }; | ||
(hook)(info); | ||
return; | ||
} | ||
|
||
if let Some(hook) = unsafe { DEFAULT_HOOK.as_ref() } { | ||
(hook)(info); | ||
} | ||
} | ||
/// Executes stored closure when dropped | ||
struct Finally<F: FnOnce()>(Option<F>); | ||
|
||
impl<F: FnOnce()> Finally<F> { | ||
fn new(body: F) -> Self { | ||
Self(Some(body)) | ||
} | ||
} | ||
|
||
impl<F: FnOnce()> Drop for Finally<F> { | ||
fn drop(&mut self) { | ||
if let Some(body) = self.0.take() { | ||
body(); | ||
} | ||
} | ||
} | ||
/// Executes main closure `body` while installing `guard` as scoped panic hook, | ||
/// for execution duration. | ||
/// | ||
/// Any panics which happen during execution of `body` are passed to `guard` hook | ||
/// to collect any info necessary, although unwind process is **NOT** interrupted. | ||
/// See module documentation for details | ||
/// | ||
/// # Parameters | ||
/// * `panic_hook` - scoped panic hook, functions for the duration of `body` execution | ||
/// * `body` - actual logic covered by `panic_hook` | ||
/// | ||
/// # Returns | ||
/// `body`'s return value | ||
pub fn with_hook<R>( | ||
mut panic_hook: impl FnMut(&PanicInfo<'_>), | ||
body: impl FnOnce() -> R, | ||
) -> R { | ||
init(); | ||
// Construct scoped hook pointer | ||
let guard_tuple = (unsafe { | ||
// `mem::transmute` is needed due to borrow checker restrictions to erase all lifetimes | ||
mem::transmute(&mut panic_hook as *mut dyn FnMut(&PanicInfo<'_>)) | ||
},); | ||
let old_tuple = SCOPED_HOOK_PTR.replace(&guard_tuple); | ||
// Old scoped hook **must** be restored before leaving function scope to keep it sound | ||
let _undo = Finally::new(|| { | ||
SCOPED_HOOK_PTR.set(old_tuple); | ||
}); | ||
body() | ||
} | ||
} | ||
|
||
#[cfg(not(feature = "handle-panics"))] | ||
mod internal { | ||
use std::panic::PanicInfo; | ||
/// Simply executes `body` and returns its execution result. | ||
/// Hook parameter is ignored | ||
pub fn with_hook<R>( | ||
_: impl FnMut(&PanicInfo<'_>), | ||
body: impl FnOnce() -> R, | ||
) -> R { | ||
body() | ||
} | ||
} | ||
|
||
pub use internal::with_hook; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we might want to add
#[allow(static_mut_refs)]
right above this to eliminate that warning.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's worth more to run full fmt+clippy check as a separate PR. There are too many warnings besides this one.