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
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
//! Static `Send`/`Sync` contract tests for the smart pointers.
//!
//! These tests do not run any code at runtime — they assert the
//! desired auto-trait behaviour at compile time. They will fail to
//! compile if a future refactor either narrows the auto-trait set
//! (e.g. by introducing a `!Send` field) or widens it past the
//! intended bounds.
use multitude::{Arc, Arena, Box};
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
// Compile-time `!Send` / `!Sync` assertions via the two-blanket-impl
// ambiguity trick: `probe` resolves unambiguously only when the type does
// *not* implement the trait. If it does, both blanket impls apply and the
// call fails to compile — exactly the regression we want to catch.
trait AmbiguousIfSend<A> {
fn probe() {}
}
impl<T: ?Sized> AmbiguousIfSend<()> for T {}
impl<T: ?Sized + Send> AmbiguousIfSend<u8> for T {}
fn assert_not_send<T: ?Sized>() {
let _ = <T as AmbiguousIfSend<_>>::probe;
}
trait AmbiguousIfSync<A> {
fn probe() {}
}
impl<T: ?Sized> AmbiguousIfSync<()> for T {}
impl<T: ?Sized + Sync> AmbiguousIfSync<u8> for T {}
fn assert_not_sync<T: ?Sized>() {
let _ = <T as AmbiguousIfSync<_>>::probe;
}
#[test]
fn arena_is_send() {
// `Arena: Send` is intended to be auto-derived from its fields.
// Lock that contract here so a future field addition that
// accidentally introduces a `!Send` type fails to compile.
// `Arena` is intentionally `!Sync` (interior `RefCell` /
// `UnsafeCell`), so only `Send` is asserted.
assert_send::<Arena>();
// And a runtime cross-thread move:
let arena: Arena = Arena::new();
let _ = arena.alloc(7_u64);
std::thread::scope(|s| {
let h = s.spawn(move || {
let x = arena.alloc(99_u64);
*x
});
assert_eq!(h.join().unwrap(), 99);
});
}
#[test]
fn arc_is_send_sync_when_t_is() {
assert_send::<Arc<u64>>();
assert_sync::<Arc<u64>>();
assert_send::<Arc<[u8]>>();
assert_sync::<Arc<[u8]>>();
let arena: Arena = Arena::new();
let a = arena.alloc_arc(42_u64);
std::thread::scope(|s| {
let h = s.spawn(move || *a);
assert_eq!(h.join().unwrap(), 42);
});
}
#[test]
fn box_is_send_sync_when_t_is() {
// `Box<T, A>` mirrors `std::Box`: `Send` when `T: Send` and
// `A: Send`; `Sync` when `T: Sync` and `A: Sync`. The backing
// storage uses an atomic-refcounted shared chunk, so dropping the
// `Box` on a different thread is sound.
assert_send::<Box<u64>>();
assert_sync::<Box<u64>>();
assert_send::<Box<[u8]>>();
assert_sync::<Box<[u8]>>();
let arena: Arena = Arena::new();
let b = arena.alloc_box(42_u64);
std::thread::scope(|s| {
let h = s.spawn(move || *b);
assert_eq!(h.join().unwrap(), 42);
});
}
#[test]
fn box_str_is_send_sync() {
// `Box<str, A>` is `Send` when `A: Send` and `Sync` when `A: Sync`
// (`str` is itself `Send + Sync`).
assert_send::<Box<str>>();
assert_sync::<Box<str>>();
let arena: Arena = Arena::new();
let b = arena.alloc_str_box("hello");
std::thread::scope(|s| {
let h = s.spawn(move || b.len());
assert_eq!(h.join().unwrap(), 5);
});
}
#[test]
fn arc_str_is_send_sync() {
assert_send::<Arc<str>>();
assert_sync::<Arc<str>>();
}
#[test]
fn drain_and_splice_are_not_send_or_sync() {
// `Vec` is thread-affine (`!Send`/`!Sync`): its `drain`/`splice`
// iterators borrow it mutably and restore the surviving tail in `Drop`.
// They must inherit that `!Send`/`!Sync` — otherwise a live `Drain`
// could migrate to another thread and run the in-place tail restore
// from a foreign thread, which is unsound. The `PhantomData<&'d mut
// Vec<..>>` marker locks this in; this test fails to compile if a future
// refactor accidentally makes either iterator `Send`/`Sync`.
use allocator_api2::alloc::Global;
use multitude::vec::{Drain, Splice};
assert_not_send::<Drain<'static, 'static, u64, Global>>();
assert_not_sync::<Drain<'static, 'static, u64, Global>>();
assert_not_send::<Splice<'static, 'static, core::iter::Empty<u64>, Global>>();
assert_not_sync::<Splice<'static, 'static, core::iter::Empty<u64>, Global>>();
}