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
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
#![deny(unsafe_op_in_unsafe_fn)]
#[cfg(feature = "c-ffi")]
use crate::src::send_sync_non_null::SendSyncNonNull;
#[cfg(feature = "c-ffi")]
use std::ffi::c_void;
#[cfg(feature = "c-ffi")]
use std::marker::PhantomData;
use std::ops::Deref;
#[cfg(feature = "c-ffi")]
use std::pin::Pin;
#[cfg(feature = "c-ffi")]
use std::ptr::NonNull;
#[cfg(feature = "c-ffi")]
use std::ptr::drop_in_place;
#[cfg(feature = "c-ffi")]
pub type FnFree = unsafe extern "C" fn(ptr: *const u8, cookie: Option<SendSyncNonNull<c_void>>);
/// A `free` "closure", i.e. a [`FnFree`] and an enclosed context [`Self::cookie`].
#[cfg(feature = "c-ffi")]
#[derive(Debug)]
pub struct Free {
pub free: FnFree,
/// # Safety
///
/// All accesses to [`Self::cookie`] must be thread-safe
/// (i.e. [`Self::cookie`] must be [`Send`]` + `[`Sync`]).
///
/// If used from Rust, [`Self::cookie`] is a [`SendSyncNonNull`],
/// whose constructors ensure this [`Send`]` + `[`Sync`] safety.
pub cookie: Option<SendSyncNonNull<c_void>>,
}
#[cfg(feature = "c-ffi")]
impl Free {
/// # Safety
///
/// `ptr` is a [`NonNull`]`<T>` and `free` deallocates it.
/// It must not be used after this call as it is deallocated.
pub unsafe fn free(&self, ptr: *mut c_void) {
// SAFETY: `self` came from `CBox::from_c`,
// which requires `self.free` to deallocate the `NonNull<T>` passed to it,
// and `self.cookie` to be passed to it, which it is.
unsafe { (self.free)(ptr as *const u8, self.cookie) }
}
}
/// Same as [`core::ptr::Unique`].
#[cfg(feature = "c-ffi")]
#[derive(Debug)]
pub struct Unique<T: ?Sized> {
pointer: NonNull<T>,
// NOTE: this marker has no consequences for variance, but is necessary
// for dropck to understand that we logically own a `T`.
//
// For details, see:
// https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data
_marker: PhantomData<T>,
}
#[cfg(feature = "c-ffi")]
/// SAFETY: [`Unique`] is [`Send`] if `T: `[`Send`]
/// because the data it references is unaliased.
unsafe impl<T: Send + ?Sized> Send for Unique<T> {}
#[cfg(feature = "c-ffi")]
/// SAFETY: [`Unique`] is [`Sync`] if `T: `[`Sync`]
unsafe impl<T: Sync + ?Sized> Sync for Unique<T> {}
/// A C/custom [`Box`].
///
/// That is, it is analogous to a [`Box`],
/// but it lets you set a C-style `free` `fn` for deallocation
/// instead of the normal [`Box`] (de)allocator.
/// It can also store a normal [`Box`] as well.
#[derive(Debug)]
pub enum CBox<T: ?Sized> {
Rust(Box<T>),
#[cfg(feature = "c-ffi")]
C {
/// # SAFETY:
///
/// * Never moved.
/// * Valid to dereference.
/// * `free`d by the `free` `fn` ptr below.
data: Unique<T>,
free: Free,
},
}
// Without c-ffi, CBox is always CBox::Rust(Box<T>), so AsRef just delegates to Box.
#[cfg(not(feature = "c-ffi"))]
impl<T: ?Sized> AsRef<T> for CBox<T> {
fn as_ref(&self) -> &T {
let Self::Rust(r#box) = self;
r#box.as_ref()
}
}
#[cfg(feature = "c-ffi")]
impl<T: ?Sized> AsRef<T> for CBox<T> {
fn as_ref(&self) -> &T {
match self {
Self::Rust(r#box) => r#box.as_ref(),
// SAFETY: `data` is a `Unique<T>`, which behaves as if it were a `T`,
// so we can take `&` references of it.
// Furthermore, `data` is never moved and is valid to dereference,
// so this reference can live as long as `CBox` and still be valid the whole time.
Self::C { data, .. } => unsafe { data.pointer.as_ref() },
}
}
}
impl<T: ?Sized> Deref for CBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}
// Without c-ffi, CBox::Rust(Box<T>) doesn't need a custom Drop — Box handles it.
// Only the C variant needs custom Drop to call the C free function.
#[cfg(feature = "c-ffi")]
impl<T: ?Sized> Drop for CBox<T> {
fn drop(&mut self) {
match self {
Self::Rust(_) => {} // Drop normally.
Self::C { data, free, .. } => {
let ptr = data.pointer.as_ptr();
// SAFETY: See below.
// The [`FnFree`] won't run Rust's `fn drop`,
// so we have to do this ourselves first.
unsafe { drop_in_place(ptr) };
let ptr = ptr.cast();
// SAFETY: See safety docs on [`Self::data`] and [`Self::from_c`].
unsafe { free.free(ptr) }
}
}
}
}
impl<T: ?Sized> CBox<T> {
/// # Safety
///
/// `data` must be valid to dereference
/// until `free.free` is called on it, which must deallocate it.
/// `free.free` is always called with `free.cookie`,
/// which must be accessed thread-safely.
#[cfg(feature = "c-ffi")]
pub unsafe fn from_c(data: NonNull<T>, free: Free) -> Self {
Self::C {
data: Unique {
pointer: data,
_marker: PhantomData,
},
free,
}
}
pub fn from_box(data: Box<T>) -> Self {
Self::Rust(data)
}
/// Pin this CBox.
///
/// Only available with c-ffi, where CArc uses `Arc<Pin<CBox<T>>>`.
/// Without c-ffi, CArc uses `Arc<Box<T>>` directly and doesn't need Pin.
#[cfg(feature = "c-ffi")]
pub fn into_pin(self) -> Pin<Self> {
// With c-ffi, CBox::C variant contains Unique<T> which is !Unpin,
// but the data is never moved until Drop, making Pin sound.
// SAFETY:
// If `self` is `Self::Rust`, `Box` can be pinned.
// If `self` is `Self::C`, `data` is never moved until [`Self::drop`].
#[allow(unsafe_code)]
unsafe {
Pin::new_unchecked(self)
}
}
}
#[cfg(feature = "c-ffi")]
impl<T: ?Sized> From<CBox<T>> for Pin<CBox<T>> {
fn from(value: CBox<T>) -> Self {
value.into_pin()
}
}