-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
fc6adf4
commit f071aee
Showing
2 changed files
with
80 additions
and
0 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
use std::rc::Rc; | ||
|
||
/// Temporarily converts a mutable reference into a reference-counted smart pointer (`Rc`). | ||
/// | ||
/// This only takes as long as the closure `f` is executed. | ||
/// | ||
/// All subsequent Rc clones must be dropped before `f` terminates, otherwise the function will panic. | ||
/// | ||
/// The `Clone` of the argument is not used, except to preserve memory consistency in case of failure. | ||
pub fn with_shared_mut_ref<T, F, R>(t: &mut T, f: F) -> R | ||
Check warning on line 10 in vm/src/with_shared.rs GitHub Actions / Contracts / Wasm tests
Check warning on line 10 in vm/src/with_shared.rs GitHub Actions / Contracts / Rust tests
Check warning on line 10 in vm/src/with_shared.rs GitHub Actions / clippy[clippy] vm/src/with_shared.rs#L10
Raw output
|
||
where | ||
T: Clone, | ||
F: FnOnce(Rc<T>) -> R, | ||
{ | ||
unsafe { | ||
// forcefully extract the owned object from the mut ref (unsafe) | ||
let obj = std::ptr::read(t); | ||
|
||
// wrap the owned object | ||
let obj_rc = Rc::new(obj); | ||
|
||
// the main action | ||
let result = f(obj_rc.clone()); | ||
|
||
// unwrapping the owned object | ||
match Rc::try_unwrap(obj_rc) { | ||
Ok(obj) => { | ||
// Rc unwrapped successfully | ||
// no need to write the owned object back to the location given by the pointer t, | ||
// because it could not have changed in the mean time, it is already there | ||
|
||
// though readonly, the object might have changed via cells, | ||
// so it needs to be copied back | ||
std::ptr::write(t, obj); | ||
}, | ||
Err(obj_rc) => { | ||
// could not unwrap, this means there are still references to obj elsewhere | ||
// to avoid memory corruption, we perform a clone of the contents | ||
let obj = (*obj_rc).clone(); | ||
std::ptr::write(t, obj); | ||
panic!("failed to recover owned object from Rc") | ||
}, | ||
} | ||
|
||
result | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod test { | ||
use std::cell::RefCell; | ||
|
||
use super::with_shared_mut_ref; | ||
|
||
#[test] | ||
fn test_with_shared_1() { | ||
let mut s = "test string".to_string(); | ||
let l = with_shared_mut_ref(&mut s, |s_rc| s_rc.len()); | ||
assert_eq!(s.len(), l); | ||
} | ||
|
||
#[test] | ||
fn test_with_shared_2() { | ||
let mut s = RefCell::new("test string".to_string()); | ||
with_shared_mut_ref(&mut s, |s_rc| { | ||
s_rc.borrow_mut().push_str(" ... changed"); | ||
}); | ||
assert_eq!(s.borrow().as_str(), "test string ... changed"); | ||
} | ||
|
||
#[test] | ||
#[should_panic = "failed to recover owned object from Rc"] | ||
fn test_with_shared_fail() { | ||
let mut s = "test string".to_string(); | ||
let _illegal_clone = with_shared_mut_ref(&mut s, |s_rc| s_rc.clone()); | ||
Check warning on line 75 in vm/src/with_shared.rs GitHub Actions / clippy[clippy] vm/src/with_shared.rs#L75
Raw output
|
||
} | ||
} |