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
//! Ownership of joltc objects: one owner type, and one destroy call per joltc type.
//!
//! [`Owned<T>`] owns either a whole joltc object or exactly one reference to a ref-counted Jolt
//! object. joltc's `*_Create` functions return such objects already holding one reference, and
//! their `*_Destroy` functions call `Release`. Each joltc type says how it is destroyed by
//! implementing [`JoltObject`] next to the code that uses it.
use std::mem::ManuallyDrop;
use std::ptr::NonNull;
/// A joltc object type that Rust owns through a pointer returned by a joltc `Create` call.
pub(crate) trait JoltObject {
/// Destroys the object, or releases the one reference its owner holds.
///
/// # Safety
/// `ptr` points to a live object of this type, and the caller owns the object or one
/// reference to it, which this call ends. Nothing uses that ownership afterwards.
unsafe fn destroy(ptr: *mut Self);
}
/// Owns one joltc object, or one reference to a ref-counted one, and destroys or releases it
/// on drop.
pub(crate) struct Owned<T: JoltObject> {
ptr: NonNull<T>,
}
impl<T: JoltObject> Owned<T> {
/// Takes over `ptr`; `None` when it is null.
///
/// # Safety
/// If not null, `ptr` points to a live `T`, and the caller hands over either sole ownership
/// of the object or exactly one reference it owns. No other owner destroys that same unit.
pub(crate) unsafe fn from_raw(ptr: *mut T) -> Option<Self> {
NonNull::new(ptr).map(|ptr| Self { ptr })
}
/// The owned object, still owned by `self`.
pub(crate) fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
/// The owned object as a `NonNull`, still owned by `self`.
pub(crate) fn as_non_null(&self) -> NonNull<T> {
self.ptr
}
/// Gives up ownership without destroying, for when joltc takes the object over.
pub(crate) fn into_raw(self) -> *mut T {
ManuallyDrop::new(self).ptr.as_ptr()
}
}
impl<T: JoltObject> Drop for Owned<T> {
fn drop(&mut self) {
// SAFETY: `from_raw` handed one ownership unit to this handle; `into_raw` consumes the
// handle without running this destructor, so the unit is ended exactly once.
unsafe { T::destroy(self.ptr.as_ptr()) }
}
}
#[cfg(test)]
mod tests {
use std::cell::Cell;
use std::rc::Rc;
use super::*;
/// A heap object that counts how often it was destroyed.
struct Probe {
destroyed: Rc<Cell<u32>>,
}
impl JoltObject for Probe {
unsafe fn destroy(ptr: *mut Self) {
// SAFETY: the tests create every probe with `Box::into_raw` and hand its one
// ownership unit to the caller, which ends it here (trait contract).
let probe = unsafe { Box::from_raw(ptr) };
probe.destroyed.set(probe.destroyed.get() + 1);
}
}
fn probe(counter: &Rc<Cell<u32>>) -> *mut Probe {
Box::into_raw(Box::new(Probe {
destroyed: Rc::clone(counter),
}))
}
#[test]
fn null_is_none() {
// SAFETY: a null pointer hands over nothing.
assert!(unsafe { Owned::<Probe>::from_raw(std::ptr::null_mut()) }.is_none());
}
#[test]
fn drop_destroys_once() {
let counter = Rc::new(Cell::new(0));
// SAFETY: the probe is live and this test owns it; the handle takes it over.
let owned = unsafe { Owned::from_raw(probe(&counter)) }.unwrap();
assert_eq!(counter.get(), 0);
drop(owned);
assert_eq!(counter.get(), 1);
}
#[test]
fn into_raw_does_not_destroy() {
let counter = Rc::new(Cell::new(0));
let raw = probe(&counter);
// SAFETY: the probe is live and this test owns it; the handle takes it over.
let owned = unsafe { Owned::from_raw(raw) }.unwrap();
assert_eq!(owned.into_raw(), raw);
assert_eq!(counter.get(), 0);
// SAFETY: `into_raw` gave the ownership back to this test, which hands it over again.
drop(unsafe { Owned::from_raw(raw) }.unwrap());
assert_eq!(counter.get(), 1);
}
}