cxx/shared_ptr.rs
1use crate::extern_type::ExternType;
2use crate::fmt::display;
3use crate::kind::Trivial;
4use crate::string::CxxString;
5use crate::unique_ptr::{UniquePtr, UniquePtrTarget};
6use crate::weak_ptr::{WeakPtr, WeakPtrTarget};
7use core::cmp::Ordering;
8use core::ffi::c_void;
9use core::fmt::{self, Debug, Display};
10use core::hash::{Hash, Hasher};
11use core::marker::PhantomData;
12use core::mem::MaybeUninit;
13use core::ops::Deref;
14use core::pin::Pin;
15use core::ptr;
16
17/// Binding to C++ `std::shared_ptr<T>`.
18///
19/// <div class="warning">
20///
21/// **WARNING:** Unlike Rust's `Arc<T>`, a C++ shared pointer manipulates
22/// pointers to 2 separate objects in general.
23///
24/// 1. One is the **managed** pointer, and its identity is associated with
25/// shared ownership of a strong and weak count shared by other SharedPtr and
26/// WeakPtr instances having the same managed pointer.
27///
28/// 2. The other is the **stored** pointer, which is commonly either the same as
29/// the managed pointer, or is a pointer into some member of the managed
30/// object, but can be any unrelated pointer in general.
31///
32/// The managed pointer is the one passed to a deleter upon the strong count
33/// reaching zero, but the stored pointer is the one accessed by deref
34/// operations and methods such as `is_null`.
35///
36/// A shared pointer is considered **empty** if the strong count is zero,
37/// meaning the managed pointer has been deleted or is about to be deleted. A
38/// shared pointer is considered **null** if the stored pointer is the null
39/// pointer. All combinations are possible. To be explicit, a shared pointer can
40/// be nonempty and nonnull, or nonempty and null, or empty and nonnull, or
41/// empty and null. In general all of these cases need to be considered when
42/// handling a SharedPtr.
43///
44/// </div>
45#[repr(C)]
46pub struct SharedPtr<T>
47where
48 T: SharedPtrTarget,
49{
50 repr: [MaybeUninit<*mut c_void>; 2],
51 ty: PhantomData<T>,
52}
53
54impl<T> SharedPtr<T>
55where
56 T: SharedPtrTarget,
57{
58 /// Makes a new SharedPtr that is both **empty** and **null**.
59 ///
60 /// Matches the behavior of default-constructing a std::shared\_ptr.
61 pub fn null() -> Self {
62 let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
63 let new = shared_ptr.as_mut_ptr().cast();
64 unsafe {
65 T::__null(new);
66 shared_ptr.assume_init()
67 }
68 }
69
70 /// Allocates memory on the heap and makes a SharedPtr owner for it.
71 ///
72 /// The shared pointer will be **nonempty** and **nonnull**.
73 pub fn new(value: T) -> Self
74 where
75 T: ExternType<Kind = Trivial>,
76 {
77 let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
78 let new = shared_ptr.as_mut_ptr().cast();
79 unsafe {
80 T::__new(value, new);
81 shared_ptr.assume_init()
82 }
83 }
84
85 /// Creates a shared pointer from a C++ heap-allocated pointer.
86 ///
87 /// Matches the behavior of std::shared\_ptr's constructor `explicit shared_ptr(T*)`.
88 ///
89 /// The SharedPtr gains ownership of the pointer and will call
90 /// `std::default_delete` on it when the refcount goes to zero.
91 ///
92 /// The object pointed to by the input pointer is not relocated by this
93 /// operation, so any pointers into this data structure elsewhere in the
94 /// program continue to be valid.
95 ///
96 /// The resulting shared pointer is **nonempty** regardless of whether the
97 /// input pointer is null, but may be either **null** or **nonnull**.
98 ///
99 /// # Panics
100 ///
101 /// Panics if `T` is an incomplete type (including `void`) or is not
102 /// destructible.
103 ///
104 /// # Safety
105 ///
106 /// Pointer must either be null or point to a valid instance of T
107 /// heap-allocated in C++ by `new`.
108 #[track_caller]
109 pub unsafe fn from_raw(raw: *mut T) -> Self {
110 let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
111 let new = shared_ptr.as_mut_ptr().cast();
112 unsafe {
113 T::__raw(new, raw);
114 shared_ptr.assume_init()
115 }
116 }
117
118 /// Checks whether the SharedPtr holds a null stored pointer.
119 ///
120 /// This is the opposite of [std::shared_ptr\<T\>::operator bool](https://en.cppreference.com/w/cpp/memory/shared_ptr/operator_bool).
121 ///
122 /// <div class="warning">
123 ///
124 /// This method is unrelated to the state of the reference count. It is
125 /// possible to have a SharedPtr that is nonnull but empty (has a refcount
126 /// of 0), typically from having been constructed using the alias
127 /// constructors in C++. Inversely, it is also possible to be null and
128 /// nonempty.
129 ///
130 /// </div>
131 pub fn is_null(&self) -> bool {
132 let this = ptr::from_ref::<Self>(self).cast::<c_void>();
133 let ptr = unsafe { T::__get(this) };
134 ptr.is_null()
135 }
136
137 /// Returns a reference to the object pointed to by the stored pointer if
138 /// nonnull, otherwise None.
139 ///
140 /// <div class="warning">
141 ///
142 /// The shared pointer's managed object may or may not already have been
143 /// destroyed.
144 ///
145 /// </div>
146 pub fn as_ref(&self) -> Option<&T> {
147 let ptr = self.as_ptr();
148 unsafe { ptr.as_ref() }
149 }
150
151 /// Returns a mutable pinned reference to the object pointed to by the
152 /// stored pointer.
153 ///
154 /// <div class="warning">
155 ///
156 /// The shared pointer's managed object may or may not already have been
157 /// destroyed.
158 ///
159 /// </div>
160 ///
161 /// # Panics
162 ///
163 /// Panics if the SharedPtr holds a null stored pointer.
164 ///
165 /// # Safety
166 ///
167 /// This method makes no attempt to ascertain the state of the reference
168 /// count. In particular, unlike `Arc::get_mut`, we do not enforce absence
169 /// of other SharedPtr and WeakPtr referring to the same data as this one.
170 /// As always, it is Undefined Behavior to have simultaneous references to
171 /// the same value while a Rust exclusive reference to it exists anywhere in
172 /// the program.
173 ///
174 /// For the special case of CXX [opaque C++ types], this method can be used
175 /// to safely call thread-safe non-const member functions on a C++ object
176 /// without regard for whether the reference is exclusive. This capability
177 /// applies only to opaque types `extern "C++" { type T; }`. It does not
178 /// apply to extern types defined with a non-opaque Rust representation
179 /// `extern "C++" { type T = ...; }`.
180 ///
181 /// [opaque C++ types]: https://cxx.rs/extern-c++.html#opaque-c-types
182 pub unsafe fn pin_mut_unchecked(&mut self) -> Pin<&mut T> {
183 let ptr = self.as_mut_ptr();
184 match unsafe { ptr.as_mut() } {
185 Some(target) => unsafe { Pin::new_unchecked(target) },
186 None => panic!(
187 "called pin_mut_unchecked on a null SharedPtr<{}>",
188 display(T::__typename),
189 ),
190 }
191 }
192
193 /// Returns the SharedPtr's stored pointer as a raw const pointer.
194 pub fn as_ptr(&self) -> *const T {
195 let this = ptr::from_ref::<Self>(self).cast::<c_void>();
196 unsafe { T::__get(this) }
197 }
198
199 /// Returns the SharedPtr's stored pointer as a raw mutable pointer.
200 ///
201 /// As with [std::shared_ptr\<T\>::get](https://en.cppreference.com/w/cpp/memory/shared_ptr/get),
202 /// this doesn't require that you hold an exclusive reference to the
203 /// SharedPtr. This differs from Rust norms, so extra care should be taken
204 /// in the way the pointer is used.
205 pub fn as_mut_ptr(&self) -> *mut T {
206 self.as_ptr().cast_mut()
207 }
208
209 /// Constructs new WeakPtr as a non-owning reference to the object managed
210 /// by `self`. If `self` manages no object, the WeakPtr manages no object
211 /// too.
212 ///
213 /// Matches the behavior of [std::weak_ptr\<T\>::weak_ptr(const std::shared_ptr\<T\> \&)](https://en.cppreference.com/w/cpp/memory/weak_ptr/weak_ptr).
214 pub fn downgrade(&self) -> WeakPtr<T>
215 where
216 T: WeakPtrTarget,
217 {
218 let this = ptr::from_ref::<Self>(self).cast::<c_void>();
219 let mut weak_ptr = MaybeUninit::<WeakPtr<T>>::uninit();
220 let new = weak_ptr.as_mut_ptr().cast();
221 unsafe {
222 T::__downgrade(this, new);
223 weak_ptr.assume_init()
224 }
225 }
226}
227
228unsafe impl<T> Send for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
229unsafe impl<T> Sync for SharedPtr<T> where T: Send + Sync + SharedPtrTarget {}
230
231impl<T> Clone for SharedPtr<T>
232where
233 T: SharedPtrTarget,
234{
235 fn clone(&self) -> Self {
236 let mut shared_ptr = MaybeUninit::<SharedPtr<T>>::uninit();
237 let new = shared_ptr.as_mut_ptr().cast();
238 let this = ptr::from_ref::<Self>(self).cast::<c_void>();
239 unsafe {
240 T::__clone(this, new);
241 shared_ptr.assume_init()
242 }
243 }
244}
245
246// SharedPtr is not a self-referential type and is safe to move out of a Pin,
247// regardless whether the pointer's target is Unpin.
248impl<T> Unpin for SharedPtr<T> where T: SharedPtrTarget {}
249
250impl<T> Drop for SharedPtr<T>
251where
252 T: SharedPtrTarget,
253{
254 fn drop(&mut self) {
255 let this = ptr::from_mut::<Self>(self).cast::<c_void>();
256 unsafe { T::__drop(this) }
257 }
258}
259
260impl<T> Deref for SharedPtr<T>
261where
262 T: SharedPtrTarget,
263{
264 type Target = T;
265
266 fn deref(&self) -> &Self::Target {
267 match self.as_ref() {
268 Some(target) => target,
269 None => panic!(
270 "called deref on a null SharedPtr<{}>",
271 display(T::__typename),
272 ),
273 }
274 }
275}
276
277impl<T> Debug for SharedPtr<T>
278where
279 T: Debug + SharedPtrTarget,
280{
281 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
282 match self.as_ref() {
283 None => formatter.write_str("nullptr"),
284 Some(value) => Debug::fmt(value, formatter),
285 }
286 }
287}
288
289impl<T> Display for SharedPtr<T>
290where
291 T: Display + SharedPtrTarget,
292{
293 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
294 match self.as_ref() {
295 None => formatter.write_str("nullptr"),
296 Some(value) => Display::fmt(value, formatter),
297 }
298 }
299}
300
301impl<T> PartialEq for SharedPtr<T>
302where
303 T: PartialEq + SharedPtrTarget,
304{
305 fn eq(&self, other: &Self) -> bool {
306 self.as_ref() == other.as_ref()
307 }
308}
309
310impl<T> Eq for SharedPtr<T> where T: Eq + SharedPtrTarget {}
311
312impl<T> PartialOrd for SharedPtr<T>
313where
314 T: PartialOrd + SharedPtrTarget,
315{
316 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
317 PartialOrd::partial_cmp(&self.as_ref(), &other.as_ref())
318 }
319}
320
321impl<T> Ord for SharedPtr<T>
322where
323 T: Ord + SharedPtrTarget,
324{
325 fn cmp(&self, other: &Self) -> Ordering {
326 Ord::cmp(&self.as_ref(), &other.as_ref())
327 }
328}
329
330impl<T> Hash for SharedPtr<T>
331where
332 T: Hash + SharedPtrTarget,
333{
334 fn hash<H>(&self, hasher: &mut H)
335 where
336 H: Hasher,
337 {
338 self.as_ref().hash(hasher);
339 }
340}
341
342impl<T> From<UniquePtr<T>> for SharedPtr<T>
343where
344 T: UniquePtrTarget + SharedPtrTarget,
345{
346 fn from(unique: UniquePtr<T>) -> Self {
347 unsafe { SharedPtr::from_raw(UniquePtr::into_raw(unique)) }
348 }
349}
350
351/// Trait bound for types which may be used as the `T` inside of a
352/// `SharedPtr<T>` in generic code.
353///
354/// This trait has no publicly callable or implementable methods. Implementing
355/// it outside of the CXX codebase is not supported.
356///
357/// # Example
358///
359/// A bound `T: SharedPtrTarget` may be necessary when manipulating
360/// [`SharedPtr`] in generic code.
361///
362/// ```
363/// use cxx::memory::{SharedPtr, SharedPtrTarget};
364/// use std::fmt::Display;
365///
366/// pub fn take_generic_ptr<T>(ptr: SharedPtr<T>)
367/// where
368/// T: SharedPtrTarget + Display,
369/// {
370/// println!("the shared_ptr points to: {}", *ptr);
371/// }
372/// ```
373///
374/// Writing the same generic function without a `SharedPtrTarget` trait bound
375/// would not compile.
376pub unsafe trait SharedPtrTarget {
377 #[doc(hidden)]
378 fn __typename(f: &mut fmt::Formatter) -> fmt::Result;
379 #[doc(hidden)]
380 unsafe fn __null(new: *mut c_void);
381 #[doc(hidden)]
382 unsafe fn __new(value: Self, new: *mut c_void)
383 where
384 Self: Sized,
385 {
386 // Opaque C types do not get this method because they can never exist by
387 // value on the Rust side of the bridge.
388 let _ = value;
389 let _ = new;
390 unreachable!()
391 }
392 #[doc(hidden)]
393 unsafe fn __raw(new: *mut c_void, raw: *mut Self);
394 #[doc(hidden)]
395 unsafe fn __clone(this: *const c_void, new: *mut c_void);
396 #[doc(hidden)]
397 unsafe fn __get(this: *const c_void) -> *const Self;
398 #[doc(hidden)]
399 unsafe fn __drop(this: *mut c_void);
400}
401
402macro_rules! impl_shared_ptr_target {
403 ($segment:expr, $name:expr, $ty:ty) => {
404 unsafe impl SharedPtrTarget for $ty {
405 fn __typename(f: &mut fmt::Formatter) -> fmt::Result {
406 f.write_str($name)
407 }
408 unsafe fn __null(new: *mut c_void) {
409 extern "C" {
410 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$null")]
411 fn __null(new: *mut c_void);
412 }
413 unsafe { __null(new) }
414 }
415 unsafe fn __new(value: Self, new: *mut c_void) {
416 extern "C" {
417 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$uninit")]
418 fn __uninit(new: *mut c_void) -> *mut c_void;
419 }
420 unsafe { __uninit(new).cast::<$ty>().write(value) }
421 }
422 unsafe fn __raw(new: *mut c_void, raw: *mut Self) {
423 extern "C" {
424 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$raw")]
425 fn __raw(new: *mut c_void, raw: *mut c_void);
426 }
427 unsafe { __raw(new, raw.cast::<c_void>()) }
428 }
429 unsafe fn __clone(this: *const c_void, new: *mut c_void) {
430 extern "C" {
431 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$clone")]
432 fn __clone(this: *const c_void, new: *mut c_void);
433 }
434 unsafe { __clone(this, new) }
435 }
436 unsafe fn __get(this: *const c_void) -> *const Self {
437 extern "C" {
438 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$get")]
439 fn __get(this: *const c_void) -> *const c_void;
440 }
441 unsafe { __get(this) }.cast()
442 }
443 unsafe fn __drop(this: *mut c_void) {
444 extern "C" {
445 #[link_name = concat!("cxxbridge1$std$shared_ptr$", $segment, "$drop")]
446 fn __drop(this: *mut c_void);
447 }
448 unsafe { __drop(this) }
449 }
450 }
451 };
452}
453
454macro_rules! impl_shared_ptr_target_for_primitive {
455 ($ty:ident) => {
456 impl_shared_ptr_target!(stringify!($ty), stringify!($ty), $ty);
457 };
458}
459
460impl_shared_ptr_target_for_primitive!(bool);
461impl_shared_ptr_target_for_primitive!(u8);
462impl_shared_ptr_target_for_primitive!(u16);
463impl_shared_ptr_target_for_primitive!(u32);
464impl_shared_ptr_target_for_primitive!(u64);
465impl_shared_ptr_target_for_primitive!(usize);
466impl_shared_ptr_target_for_primitive!(i8);
467impl_shared_ptr_target_for_primitive!(i16);
468impl_shared_ptr_target_for_primitive!(i32);
469impl_shared_ptr_target_for_primitive!(i64);
470impl_shared_ptr_target_for_primitive!(isize);
471impl_shared_ptr_target_for_primitive!(f32);
472impl_shared_ptr_target_for_primitive!(f64);
473
474impl_shared_ptr_target!("string", "CxxString", CxxString);