1use core::alloc::Layout;
2
3use crate::{
4 Def, Facet, KnownSmartPointer, PtrConst, PtrMut, PtrUninit, Shape, ShapeLayout,
5 SmartPointerDef, SmartPointerFlags, SmartPointerVTable, TryBorrowInnerError, TryFromError,
6 TryIntoInnerError, Type, UserType, ValueVTable, value_vtable,
7};
8
9unsafe impl<'a, T: Facet<'a> + ?Sized> Facet<'a> for alloc::sync::Arc<T> {
10 const VTABLE: &'static ValueVTable = &const {
11 unsafe fn try_from<'a, 'shape, 'src, 'dst, T: Facet<'a> + ?Sized>(
13 src_ptr: PtrConst<'src>,
14 src_shape: &'shape Shape<'shape>,
15 dst: PtrUninit<'dst>,
16 ) -> Result<PtrMut<'dst>, TryFromError<'shape>> {
17 if src_shape.id != T::SHAPE.id {
18 return Err(TryFromError::UnsupportedSourceShape {
19 src_shape,
20 expected: &[T::SHAPE],
21 });
22 }
23
24 if let ShapeLayout::Unsized = T::SHAPE.layout {
25 panic!("can't try_from with unsized type");
26 }
27
28 use alloc::sync::Arc;
29
30 let layout = match T::SHAPE.layout {
32 ShapeLayout::Sized(layout) => layout,
33 ShapeLayout::Unsized => panic!("Unsized type not supported"),
34 };
35
36 let size_of_arc_header = core::mem::size_of::<usize>() * 2;
38
39 let header_layout =
41 Layout::from_size_align(size_of_arc_header, core::mem::align_of::<usize>())
42 .unwrap();
43 let (arc_layout, value_offset) = header_layout.extend(layout).unwrap();
44
45 let adjusted_size = (arc_layout.size() + 7) & !7;
48 let final_layout =
49 unsafe { Layout::from_size_align_unchecked(adjusted_size, arc_layout.align()) };
50
51 let mem = unsafe { alloc::alloc::alloc(final_layout) };
52
53 unsafe {
54 let dummy_arc = Arc::new(());
56 let header_start = (Arc::as_ptr(&dummy_arc) as *const u8).sub(size_of_arc_header);
57 core::ptr::copy_nonoverlapping(header_start, mem, size_of_arc_header);
58
59 core::ptr::copy_nonoverlapping(
61 src_ptr.as_byte_ptr(),
62 mem.add(value_offset),
63 layout.size(),
64 );
65 }
66
67 let ptr = unsafe { mem.add(value_offset) };
69 let t_ptr: *mut T = unsafe { core::mem::transmute_copy(&ptr) };
70 let arc = unsafe { Arc::from_raw(t_ptr) };
71
72 Ok(unsafe { dst.put(arc) })
74 }
75
76 unsafe fn try_into_inner<'a, 'src, 'dst, T: Facet<'a> + ?Sized>(
77 src_ptr: PtrMut<'src>,
78 dst: PtrUninit<'dst>,
79 ) -> Result<PtrMut<'dst>, TryIntoInnerError> {
80 use alloc::sync::Arc;
81
82 let mut arc = unsafe { src_ptr.read::<Arc<T>>() };
84
85 let size = match T::SHAPE.layout {
87 ShapeLayout::Sized(layout) => layout.size(),
88 _ => panic!("cannot try_into_inner with unsized type"),
89 };
90
91 if let Some(inner_ref) = Arc::get_mut(&mut arc) {
93 let inner_ptr = inner_ref as *const T as *const u8;
95
96 unsafe {
97 core::ptr::copy_nonoverlapping(inner_ptr, dst.as_mut_byte_ptr(), size);
99
100 let raw_ptr = Arc::into_raw(arc);
103
104 let size_of_arc_header = core::mem::size_of::<usize>() * 2;
107 let layout = match T::SHAPE.layout {
108 ShapeLayout::Sized(layout) => layout,
109 _ => unreachable!("We already checked that T is sized"),
110 };
111 let arc_layout = Layout::from_size_align_unchecked(
112 size_of_arc_header + size,
113 layout.align(),
114 );
115
116 let allocation_start = (raw_ptr as *mut u8).sub(size_of_arc_header);
118
119 alloc::alloc::dealloc(allocation_start, arc_layout);
121
122 Ok(PtrMut::new(dst.as_mut_byte_ptr()))
124 }
125 } else {
126 core::mem::forget(arc);
128 Err(TryIntoInnerError::Unavailable)
129 }
130 }
131
132 unsafe fn try_borrow_inner<'a, 'src, T: Facet<'a> + ?Sized>(
133 src_ptr: PtrConst<'src>,
134 ) -> Result<PtrConst<'src>, TryBorrowInnerError> {
135 let arc = unsafe { src_ptr.get::<alloc::sync::Arc<T>>() };
136 Ok(PtrConst::new(&**arc))
137 }
138
139 let mut vtable = value_vtable!(alloc::sync::Arc<T>, |f, opts| {
140 write!(f, "Arc")?;
141 if let Some(opts) = opts.for_children() {
142 write!(f, "<")?;
143 (T::SHAPE.vtable.type_name)(f, opts)?;
144 write!(f, ">")?;
145 } else {
146 write!(f, "<…>")?;
147 }
148 Ok(())
149 });
150
151 vtable.try_from = Some(try_from::<T>);
152 vtable.try_into_inner = Some(try_into_inner::<T>);
153 vtable.try_borrow_inner = Some(try_borrow_inner::<T>);
154 vtable
155 };
156
157 const SHAPE: &'static crate::Shape<'static> = &const {
158 fn inner_shape<'a, T: Facet<'a> + ?Sized>() -> &'static Shape<'static> {
160 T::SHAPE
161 }
162
163 crate::Shape::builder_for_sized::<Self>()
164 .type_params(&[crate::TypeParam {
165 name: "T",
166 shape: || T::SHAPE,
167 }])
168 .ty(Type::User(UserType::Opaque))
169 .def(Def::SmartPointer(
170 SmartPointerDef::builder()
171 .pointee(|| T::SHAPE)
172 .flags(SmartPointerFlags::ATOMIC)
173 .known(KnownSmartPointer::Arc)
174 .weak(|| <alloc::sync::Weak<T> as Facet>::SHAPE)
175 .vtable(
176 &const {
177 SmartPointerVTable::builder()
178 .borrow_fn(|this| {
179 let ptr = Self::as_ptr(unsafe { this.get() });
180 PtrConst::new(ptr)
181 })
182 .new_into_fn(|this, ptr| {
183 use alloc::sync::Arc;
184
185 let layout = match T::SHAPE.layout {
186 ShapeLayout::Sized(layout) => layout,
187 ShapeLayout::Unsized => panic!("nope"),
188 };
189
190 let size_of_arc_header = core::mem::size_of::<usize>() * 2;
191
192 let arc_layout = unsafe {
195 Layout::from_size_align_unchecked(
196 size_of_arc_header + layout.size(),
197 layout.align(),
198 )
199 };
200 let mem = unsafe { alloc::alloc::alloc(arc_layout) };
201
202 unsafe {
203 let dummy_arc = alloc::sync::Arc::new(());
206 let header_start = (Arc::as_ptr(&dummy_arc) as *const u8)
207 .sub(size_of_arc_header);
208 core::ptr::copy_nonoverlapping(
209 header_start,
210 mem,
211 size_of_arc_header,
212 );
213
214 core::ptr::copy_nonoverlapping(
216 ptr.as_byte_ptr(),
217 mem.add(size_of_arc_header),
218 layout.size(),
219 );
220 }
221
222 let ptr = unsafe { mem.add(size_of_arc_header) };
224 let t_ptr: *mut T = unsafe { core::mem::transmute_copy(&ptr) };
225 let arc = unsafe { Arc::from_raw(t_ptr) };
227 unsafe { this.put(arc) }
229 })
230 .downgrade_into_fn(|strong, weak| unsafe {
231 weak.put(alloc::sync::Arc::downgrade(strong.get::<Self>()))
232 })
233 .build()
234 },
235 )
236 .build(),
237 ))
238 .inner(inner_shape::<T>)
239 .build()
240 };
241}
242
243unsafe impl<'a, T: Facet<'a> + ?Sized> Facet<'a> for alloc::sync::Weak<T> {
244 const VTABLE: &'static ValueVTable = &const {
245 value_vtable!(alloc::sync::Weak<T>, |f, opts| {
246 write!(f, "Weak")?;
247 if let Some(opts) = opts.for_children() {
248 write!(f, "<")?;
249 (T::SHAPE.vtable.type_name)(f, opts)?;
250 write!(f, ">")?;
251 } else {
252 write!(f, "<…>")?;
253 }
254 Ok(())
255 })
256 };
257
258 const SHAPE: &'static crate::Shape<'static> = &const {
259 fn inner_shape<'a, T: Facet<'a> + ?Sized>() -> &'static Shape<'static> {
261 T::SHAPE
262 }
263
264 crate::Shape::builder_for_sized::<Self>()
265 .type_params(&[crate::TypeParam {
266 name: "T",
267 shape: || T::SHAPE,
268 }])
269 .ty(Type::User(UserType::Opaque))
270 .def(Def::SmartPointer(
271 SmartPointerDef::builder()
272 .pointee(|| T::SHAPE)
273 .flags(SmartPointerFlags::ATOMIC.union(SmartPointerFlags::WEAK))
274 .known(KnownSmartPointer::ArcWeak)
275 .strong(|| <alloc::sync::Arc<T> as Facet>::SHAPE)
276 .vtable(
277 &const {
278 SmartPointerVTable::builder()
279 .upgrade_into_fn(|weak, strong| unsafe {
280 Some(strong.put(weak.get::<Self>().upgrade()?))
281 })
282 .build()
283 },
284 )
285 .build(),
286 ))
287 .inner(inner_shape::<T>)
288 .build()
289 };
290}
291
292#[cfg(test)]
293mod tests {
294 use alloc::string::String;
295 use alloc::sync::{Arc, Weak as ArcWeak};
296
297 use super::*;
298
299 #[test]
300 fn test_arc_type_params() {
301 let [type_param_1] = <Arc<i32>>::SHAPE.type_params else {
302 panic!("Arc<T> should only have 1 type param")
303 };
304 assert_eq!(type_param_1.shape(), i32::SHAPE);
305 }
306
307 #[test]
308 fn test_arc_vtable_1_new_borrow_drop() -> eyre::Result<()> {
309 facet_testhelpers::setup();
310
311 let arc_shape = <Arc<String>>::SHAPE;
312 let arc_def = arc_shape
313 .def
314 .into_smart_pointer()
315 .expect("Arc<T> should have a smart pointer definition");
316
317 let arc_uninit_ptr = arc_shape.allocate()?;
319
320 let new_into_fn = arc_def
322 .vtable
323 .new_into_fn
324 .expect("Arc<T> should have new_into_fn");
325
326 let mut value = String::from("example");
328 let arc_ptr = unsafe { new_into_fn(arc_uninit_ptr, PtrMut::new(&raw mut value)) };
329 core::mem::forget(value);
331
332 let borrow_fn = arc_def
334 .vtable
335 .borrow_fn
336 .expect("Arc<T> should have borrow_fn");
337
338 let borrowed_ptr = unsafe { borrow_fn(arc_ptr.as_const()) };
340 assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "example");
342
343 let drop_fn = arc_shape
345 .vtable
346 .drop_in_place
347 .expect("Arc<T> should have drop_in_place");
348
349 unsafe { drop_fn(arc_ptr) };
352
353 unsafe { arc_shape.deallocate_mut(arc_ptr)? };
356
357 Ok(())
358 }
359
360 #[test]
361 fn test_arc_vtable_2_downgrade_upgrade_drop() -> eyre::Result<()> {
362 facet_testhelpers::setup();
363
364 let arc_shape = <Arc<String>>::SHAPE;
365 let arc_def = arc_shape
366 .def
367 .into_smart_pointer()
368 .expect("Arc<T> should have a smart pointer definition");
369
370 let weak_shape = <ArcWeak<String>>::SHAPE;
371 let weak_def = weak_shape
372 .def
373 .into_smart_pointer()
374 .expect("ArcWeak<T> should have a smart pointer definition");
375
376 let arc1_uninit_ptr = arc_shape.allocate()?;
378 let new_into_fn = arc_def.vtable.new_into_fn.unwrap();
379 let mut value = String::from("example");
380 let arc1_ptr = unsafe { new_into_fn(arc1_uninit_ptr, PtrMut::new(&raw mut value)) };
381 core::mem::forget(value); let weak1_uninit_ptr = weak_shape.allocate()?;
385 let downgrade_into_fn = arc_def.vtable.downgrade_into_fn.unwrap();
386 let weak1_ptr = unsafe { downgrade_into_fn(arc1_ptr, weak1_uninit_ptr) };
388
389 let arc2_uninit_ptr = arc_shape.allocate()?;
391 let upgrade_into_fn = weak_def.vtable.upgrade_into_fn.unwrap();
392 let arc2_ptr = unsafe { upgrade_into_fn(weak1_ptr, arc2_uninit_ptr) }
395 .expect("Upgrade should succeed while original Arc exists");
396
397 let borrow_fn = arc_def.vtable.borrow_fn.unwrap();
399 let borrowed_ptr = unsafe { borrow_fn(arc2_ptr.as_const()) };
401 assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "example");
403
404 let arc_drop_fn = arc_shape.vtable.drop_in_place.unwrap();
406 let weak_drop_fn = weak_shape.vtable.drop_in_place.unwrap();
407
408 unsafe {
409 arc_drop_fn(arc1_ptr);
411 arc_shape.deallocate_mut(arc1_ptr)?;
412 arc_drop_fn(arc2_ptr);
413 arc_shape.deallocate_mut(arc2_ptr)?;
414
415 weak_drop_fn(weak1_ptr);
417 weak_shape.deallocate_mut(weak1_ptr)?;
418 }
419
420 Ok(())
421 }
422
423 #[test]
424 fn test_arc_vtable_3_downgrade_drop_try_upgrade() -> eyre::Result<()> {
425 facet_testhelpers::setup();
426
427 let arc_shape = <Arc<String>>::SHAPE;
428 let arc_def = arc_shape
429 .def
430 .into_smart_pointer()
431 .expect("Arc<T> should have a smart pointer definition");
432
433 let weak_shape = <ArcWeak<String>>::SHAPE;
434 let weak_def = weak_shape
435 .def
436 .into_smart_pointer()
437 .expect("ArcWeak<T> should have a smart pointer definition");
438
439 let arc1_uninit_ptr = arc_shape.allocate()?;
441 let new_into_fn = arc_def.vtable.new_into_fn.unwrap();
442 let mut value = String::from("example");
443 let arc1_ptr = unsafe { new_into_fn(arc1_uninit_ptr, PtrMut::new(&raw mut value)) };
444 core::mem::forget(value);
445
446 let weak1_uninit_ptr = weak_shape.allocate()?;
448 let downgrade_into_fn = arc_def.vtable.downgrade_into_fn.unwrap();
449 let weak1_ptr = unsafe { downgrade_into_fn(arc1_ptr, weak1_uninit_ptr) };
451
452 let arc_drop_fn = arc_shape.vtable.drop_in_place.unwrap();
454 unsafe {
455 arc_drop_fn(arc1_ptr);
456 arc_shape.deallocate_mut(arc1_ptr)?;
457 }
458
459 let upgrade_into_fn = weak_def.vtable.upgrade_into_fn.unwrap();
461 let arc2_uninit_ptr = arc_shape.allocate()?;
462 let upgrade_result = unsafe { upgrade_into_fn(weak1_ptr, arc2_uninit_ptr) };
464
465 assert!(
467 upgrade_result.is_none(),
468 "Upgrade should fail after the strong Arc is dropped"
469 );
470
471 let weak_drop_fn = weak_shape.vtable.drop_in_place.unwrap();
473 unsafe {
474 arc_shape.deallocate_uninit(arc2_uninit_ptr)?;
476
477 weak_drop_fn(weak1_ptr);
479 weak_shape.deallocate_mut(weak1_ptr)?;
480 }
481
482 Ok(())
483 }
484
485 #[test]
486 fn test_arc_vtable_4_try_from() -> eyre::Result<()> {
487 facet_testhelpers::setup();
488
489 let string_shape = <String>::SHAPE;
491 let arc_shape = <Arc<String>>::SHAPE;
492 let arc_def = arc_shape
493 .def
494 .into_smart_pointer()
495 .expect("Arc<T> should have a smart pointer definition");
496
497 let value = String::from("try_from test");
499 let value_ptr = PtrConst::new(&value as *const String as *const u8);
500
501 let arc_uninit_ptr = arc_shape.allocate()?;
503
504 let try_from_fn = arc_shape
506 .vtable
507 .try_from
508 .expect("Arc<T> should have try_from");
509
510 let arc_ptr = unsafe { try_from_fn(value_ptr, string_shape, arc_uninit_ptr) }
512 .expect("try_from should succeed");
513 core::mem::forget(value);
514
515 let borrow_fn = arc_def
517 .vtable
518 .borrow_fn
519 .expect("Arc<T> should have borrow_fn");
520 let borrowed_ptr = unsafe { borrow_fn(arc_ptr.as_const()) };
521
522 assert_eq!(unsafe { borrowed_ptr.get::<String>() }, "try_from test");
524
525 let drop_fn = arc_shape
527 .vtable
528 .drop_in_place
529 .expect("Arc<T> should have drop_in_place");
530
531 unsafe {
532 drop_fn(arc_ptr);
533 arc_shape.deallocate_mut(arc_ptr)?;
534 }
535
536 Ok(())
537 }
538
539 #[test]
540 fn test_arc_vtable_5_try_into_inner() -> eyre::Result<()> {
541 facet_testhelpers::setup();
542
543 let string_shape = <String>::SHAPE;
545 let arc_shape = <Arc<String>>::SHAPE;
546 let arc_def = arc_shape
547 .def
548 .into_smart_pointer()
549 .expect("Arc<T> should have a smart pointer definition");
550
551 let arc_uninit_ptr = arc_shape.allocate()?;
553 let new_into_fn = arc_def
554 .vtable
555 .new_into_fn
556 .expect("Arc<T> should have new_into_fn");
557
558 let mut value = String::from("try_into_inner test");
559 let arc_ptr = unsafe { new_into_fn(arc_uninit_ptr, PtrMut::new(&raw mut value)) };
560 core::mem::forget(value); let string_uninit_ptr = string_shape.allocate()?;
564
565 let try_into_inner_fn = arc_shape
567 .vtable
568 .try_into_inner
569 .expect("Arc<T> Shape should have try_into_inner");
570
571 let string_ptr = unsafe { try_into_inner_fn(arc_ptr, string_uninit_ptr) }
574 .expect("try_into_inner should succeed with exclusive access");
575
576 assert_eq!(
578 unsafe { string_ptr.as_const().get::<String>() },
579 "try_into_inner test"
580 );
581
582 let string_drop_fn = string_shape
584 .vtable
585 .drop_in_place
586 .expect("String should have drop_in_place");
587
588 unsafe {
589 arc_shape.deallocate_mut(arc_ptr)?;
592
593 string_drop_fn(string_ptr);
595 string_shape.deallocate_mut(string_ptr)?;
596 }
597
598 Ok(())
599 }
600}