-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathunsafe_self_cell.rs
152 lines (122 loc) · 5.26 KB
/
unsafe_self_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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use core::marker::PhantomData;
use core::mem::transmute;
use core::ptr::{drop_in_place, read, NonNull};
extern crate alloc;
use alloc::alloc::{dealloc, Layout};
// Self referential structs are currently not supported with safe vanilla Rust.
// The only reasonable safe alternative is to expect the user to juggle 2 separate
// data structures which is a mess. The library solution rental is both no longer
// maintained and really heavy to compile. So begrudgingly I rolled my own version.
// These are some of the core invariants we require for this to be safe to use.
//
// 1. owner is initialized when UnsafeSelfCell is constructed.
// 2. owner is NEVER changed again.
// 3. The pointer to owner and dependent never changes, even when moved.
// 4. The only access to owner and dependent is as immutable reference.
// 5. owner lives longer than dependent.
#[doc(hidden)]
pub struct JoinedCell<Owner, Dependent> {
pub owner: Owner,
pub dependent: Dependent,
}
// Library controlled struct that marks all accesses as unsafe.
// Because the macro generated struct impl can be extended, could be unsafe.
#[doc(hidden)]
pub struct UnsafeSelfCell<ContainedIn, Owner, DependentStatic: 'static> {
joined_void_ptr: NonNull<u8>,
// ContainedIn is necessary for type safety since we don't fully
// prohibit access to the UnsafeSelfCell; swapping between different
// structs can be unsafe otherwise, see Issue #17.
contained_in_marker: PhantomData<ContainedIn>,
owner_marker: PhantomData<Owner>,
// DependentStatic is only used to correctly derive Send and Sync.
dependent_marker: PhantomData<DependentStatic>,
}
impl<ContainedIn, Owner, DependentStatic> UnsafeSelfCell<ContainedIn, Owner, DependentStatic> {
pub unsafe fn new(joined_void_ptr: NonNull<u8>) -> Self {
Self {
joined_void_ptr,
contained_in_marker: PhantomData,
owner_marker: PhantomData,
dependent_marker: PhantomData,
}
}
pub unsafe fn borrow_owner<'a, Dependent>(&'a self) -> &'a Owner {
let joined_ptr =
transmute::<NonNull<u8>, NonNull<JoinedCell<Owner, Dependent>>>(self.joined_void_ptr);
&(*joined_ptr.as_ptr()).owner
}
pub unsafe fn borrow_dependent<'a, Dependent>(&'a self) -> &'a Dependent {
let joined_ptr =
transmute::<NonNull<u8>, NonNull<JoinedCell<Owner, Dependent>>>(self.joined_void_ptr);
&(*joined_ptr.as_ptr()).dependent
}
pub unsafe fn borrow_mut<'a, Dependent>(&'a mut self) -> &'a mut JoinedCell<Owner, Dependent> {
let joined_ptr =
transmute::<NonNull<u8>, NonNull<JoinedCell<Owner, Dependent>>>(self.joined_void_ptr);
&mut (*joined_ptr.as_ptr())
}
// Any subsequent use of this struct other than dropping it is UB.
pub unsafe fn drop_joined<Dependent>(&mut self) {
let joined_ptr =
transmute::<NonNull<u8>, NonNull<JoinedCell<Owner, Dependent>>>(self.joined_void_ptr);
drop_in_place(joined_ptr.as_ptr());
let layout = Layout::new::<JoinedCell<Owner, Dependent>>();
dealloc(self.joined_void_ptr.as_ptr(), layout);
}
pub unsafe fn into_owner<Dependent>(self) -> Owner {
let joined_ptr =
transmute::<NonNull<u8>, NonNull<JoinedCell<Owner, Dependent>>>(self.joined_void_ptr);
let owner_ptr: *const Owner = &(*joined_ptr.as_ptr()).owner;
// Move owner out so it can be returned.
let owner = read(owner_ptr);
// Clean up rest of JoinedCell
drop_in_place(&mut (*joined_ptr.as_ptr()).dependent);
let layout = Layout::new::<JoinedCell<Owner, Dependent>>();
dealloc(self.joined_void_ptr.as_ptr(), layout);
owner
}
}
unsafe impl<ContainedIn, Owner, DependentStatic> Send
for UnsafeSelfCell<ContainedIn, Owner, DependentStatic>
where
// Only derive Send if Owner and DependentStatic is also Send
Owner: Send,
DependentStatic: Send,
{
}
unsafe impl<ContainedIn, Owner, DependentStatic> Sync
for UnsafeSelfCell<ContainedIn, Owner, DependentStatic>
where
// Only derive Sync if Owner and DependentStatic is also Sync
Owner: Sync,
DependentStatic: Sync,
{
}
// This struct is used to safely deallocate only the owner if dependent
// construction fails.
//
// mem::forget it once it's no longer needed or dtor will be UB.
#[doc(hidden)]
pub struct OwnerAndCellDropGuard<Owner, Dependent> {
joined_ptr: NonNull<JoinedCell<Owner, Dependent>>,
}
impl<Owner, Dependent> OwnerAndCellDropGuard<Owner, Dependent> {
pub unsafe fn new(joined_ptr: NonNull<JoinedCell<Owner, Dependent>>) -> Self {
Self { joined_ptr }
}
}
impl<Owner, Dependent> Drop for OwnerAndCellDropGuard<Owner, Dependent> {
fn drop(&mut self) {
unsafe {
// We must only drop owner and the struct itself,
// The whole point of this drop guard is to clean up the partially
// initialized struct should building the dependent fail.
drop_in_place(&mut (*self.joined_ptr.as_ptr()).owner);
let layout = Layout::new::<JoinedCell<Owner, Dependent>>();
let joined_void_ptr =
transmute::<*mut JoinedCell<Owner, Dependent>, *mut u8>(self.joined_ptr.as_ptr());
dealloc(joined_void_ptr, layout);
}
}
}