This repository has been archived by the owner on Jan 21, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathmove_cell.rs
127 lines (113 loc) · 3.33 KB
/
move_cell.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use std::cell::UnsafeCell;
use std::mem;
use std::rc::{Rc, Weak};
/// Like `Cell<T>`, but doesn’t require `T: Copy`.
/// Specialization of https://github.com/SimonSapin/rust-movecell
pub struct MoveCell<T>(UnsafeCell<T>);
impl<T> MoveCell<T> {
#[inline]
pub fn new(x: T) -> Self {
MoveCell(UnsafeCell::new(x))
}
// Note: this is unsound:
//
// #[inline]
// pub fn set(&self, x: T) {
// unsafe {
// *self.0.get() = x;
// }
// }
//
// Example:
//
// struct Evil(Box<u32>, Rc<MoveCell<Option<Evil>>>);
// impl Drop for Evil {
// fn drop(&mut self) {
// mem::drop(self.1.take()); // Mess with the "other" node, which might be `self`.
// self.0.clone(); // use after free!
// }
// }
// let a = Rc::new(MoveCell::new(None));
// a.replace(Some(Evil(Box::new(5), a.clone()))); // Make a reference cycle.
// a.set(None); // Trigger Evil::drop while in the cell
#[inline]
pub fn replace(&self, x: T) -> T {
unsafe {
mem::replace(&mut *self.0.get(), x)
}
}
}
impl<T> MoveCell<Option<T>> {
#[inline]
pub fn is_none(&self) -> bool {
unsafe {
(*self.0.get()).is_none()
}
}
#[inline]
pub fn take(&self) -> Option<T> {
unsafe {
(*self.0.get()).take()
}
}
}
impl<T> MoveCell<Option<Weak<T>>> {
#[inline]
pub fn upgrade(&self) -> Option<Rc<T>> {
unsafe {
match *self.0.get() {
Some(ref weak) => weak.upgrade(),
None => None,
}
}
}
}
impl<T> MoveCell<Option<Rc<T>>> {
/// Return `Some` if this `Rc` is the only strong reference count,
/// even if there are weak references.
#[inline]
pub fn take_if_unique_strong(&self) -> Option<Rc<T>> {
unsafe {
match *self.0.get() {
None => None,
Some(ref rc) if Rc::strong_count(rc) > 1 => None,
// Not borrowing the `Rc<T>` here
// as we would be invalidating that borrow while it is outstanding:
Some(_) => self.take(),
}
}
}
}
impl<T> MoveCell<T> where T: WellBehavedClone {
#[inline]
pub fn clone_inner(&self) -> T {
unsafe {
(*self.0.get()).clone()
}
}
}
/**
A Clone impl that will not access the cell again through reference cycles,
which would introduce mutable aliasing.
Incorrect example:
```rust
struct Evil(Box<u32>, Rc<MoveCell<Option<Evil>>>);
impl Clone for Evil {
fn clone(&self) -> Self {
mem::drop(self.1.take()); // Mess with the "other" node, which might be `self`.
Evil(
self.0.clone(), // use after free!
Rc::new(MoveCell::new(None))
)
}
}
unsafe impl WellBehavedClone for Evil {} // Wrong.
let a = Rc::new(MoveCell::new(None));
a.set(Some(Evil(Box::new(5), a.clone()))); // Make a reference cycle.
a.clone_inner();
```
*/
pub unsafe trait WellBehavedClone: Clone {}
unsafe impl<T> WellBehavedClone for Rc<T> {}
unsafe impl<T> WellBehavedClone for Weak<T> {}
unsafe impl<T> WellBehavedClone for Option<T> where T: WellBehavedClone {}