@@ -74,7 +74,184 @@ of their pervasive use of raw pointers to manage allocations and complex ownersh
7474Similarly, most iterators into these collections are Send and Sync because they
7575largely behave like an ` & ` or ` &mut ` into the collection.
7676
77+ ## Example
78+
79+ [ ` Box ` ] [ box-doc ] is implemented as it's own special intrinsic type by the
80+ compiler for [ various reasons] [ box-is-special ] , but we can implement something
81+ with similar-ish behavior ourselves to see an example of when it is sound to
82+ implement Send and Sync. Let's call it a ` Carton ` .
83+
84+ We start by writing code to take a value allocated on the stack and transfer it
85+ to the heap.
86+
87+ ``` rust
88+ # pub mod libc {
89+ # pub use :: std :: os :: raw :: {c_int, c_void};
90+ # #[allow(non_camel_case_types)]
91+ # pub type size_t = usize ;
92+ # extern " C" { pub fn posix_memalign (memptr : * mut * mut c_void , align : size_t , size : size_t ) -> c_int ; }
93+ # }
94+ use std :: {
95+ mem :: {align_of, size_of},
96+ ptr,
97+ };
98+
99+ struct Carton <T >(ptr :: NonNull <T >);
100+
101+ impl <T > Carton <T > {
102+ pub fn new (value : T ) -> Self {
103+ // Allocate enough memory on the heap to store one T.
104+ assert_ne! (size_of :: <T >(), 0 , " Zero-sized types are out of the scope of this example" );
105+ let mut memptr = ptr :: null_mut () as * mut T ;
106+ unsafe {
107+ let ret = libc :: posix_memalign (
108+ (& mut memptr ). cast (),
109+ align_of :: <T >(),
110+ size_of :: <T >()
111+ );
112+ assert_eq! (ret , 0 , " Failed to allocate or invalid alignment" );
113+ };
114+
115+ // NonNull is just a wrapper that enforces that the pointer isn't null.
116+ let mut ptr = unsafe {
117+ // Safety: memptr is dereferenceable because we created it from a
118+ // reference and have exclusive access.
119+ ptr :: NonNull :: new (memptr . cast :: <T >())
120+ . expect (" Guaranteed non-null if posix_memalign returns 0" )
121+ };
122+
123+ // Move value from the stack to the location we allocated on the heap.
124+ unsafe {
125+ // Safety: If non-null, posix_memalign gives us a ptr that is valid
126+ // for writes and properly aligned.
127+ ptr . as_ptr (). write (value );
128+ }
129+
130+ Self (ptr )
131+ }
132+ }
133+ ```
134+
135+ This isn't very useful, because once our users give us a value they have no way
136+ to access it. [ ` Box ` ] [ box-doc ] implements [ ` Deref ` ] [ deref-doc ] and
137+ [ ` DerefMut ` ] [ deref-mut-doc ] so that you can access the inner value. Let's do
138+ that.
139+
140+ ``` rust
141+ use std :: ops :: {Deref , DerefMut };
142+
143+ # struct Carton <T >(std :: ptr :: NonNull <T >);
144+ #
145+ impl <T > Deref for Carton <T > {
146+ type Target = T ;
147+
148+ fn deref (& self ) -> & Self :: Target {
149+ unsafe {
150+ // Safety: The pointer is aligned, initialized, and dereferenceable
151+ // by the logic in [`Self::new`]. We require writers to borrow the
152+ // Carton, and the lifetime of the return value is elided to the
153+ // lifetime of the input. This means the borrow checker will
154+ // enforce that no one can mutate the contents of the Carton until
155+ // the reference returned is dropped.
156+ self . 0. as_ref ()
157+ }
158+ }
159+ }
160+
161+ impl <T > DerefMut for Carton <T > {
162+ fn deref_mut (& mut self ) -> & mut Self :: Target {
163+ unsafe {
164+ // Safety: The pointer is aligned, initialized, and dereferenceable
165+ // by the logic in [`Self::new`]. We require writers to mutably
166+ // borrow the Carton, and the lifetime of the return value is
167+ // elided to the lifetime of the input. This means the borrow
168+ // checker will enforce that no one else can access the contents
169+ // of the Carton until the mutable reference returned is dropped.
170+ self . 0. as_mut ()
171+ }
172+ }
173+ }
174+ ```
175+
176+ Finally, let's think about whether our ` Carton ` is Send and Sync. Something can
177+ safely be Send unless it shares mutable state with something else without
178+ enforcing exclusive access to it. Each ` Carton ` has a unique pointer, so
179+ we're good.
180+
181+ ``` rust
182+ # struct Carton <T >(std :: ptr :: NonNull <T >);
183+ // Safety: No one besides us has the raw pointer, so we can safely transfer the
184+ // Carton to another thread if T can be safely transferred.
185+ unsafe impl <T > Send for Carton <T > where T : Send {}
186+ ```
187+
188+ What about Sync? For ` Carton ` to be Sync we have to enforce that you can't
189+ write to something stored in a ` &Carton ` while that same something could be read
190+ or written to from another ` &Carton ` . Since you need an ` &mut Carton ` to
191+ write to the pointer, and the borrow checker enforces that mutable
192+ references must be exclusive, there are no soundness issues making ` Carton `
193+ sync either.
194+
195+ ``` rust
196+ # struct Carton <T >(std :: ptr :: NonNull <T >);
197+ // Safety: Since there exists a public way to go from a `&Carton<T>` to a `&T`
198+ // in an unsynchronized fashion (such as `Deref`), then `Carton<T>` can't be
199+ // `Sync` if `T` isn't.
200+ // Conversely, `Carton` itself does not use any interior mutability whatsoever:
201+ // all the mutations are performed through an exclusive reference (`&mut`). This
202+ // means it suffices that `T` be `Sync` for `Carton<T>` to be `Sync`:
203+ unsafe impl <T > Sync for Carton <T > where T : Sync {}
204+ ```
205+
206+ When we assert our type is Send and Sync we usually need to enforce that every
207+ contained type is Send and Sync. When writing custom types that behave like
208+ standard library types we can assert that we have the same requirements.
209+ For example, the following code asserts that a Carton is Send if the same
210+ sort of Box would be Send, which in this case is the same as saying T is Send.
211+
212+ ``` rust
213+ # struct Carton <T >(std :: ptr :: NonNull <T >);
214+ unsafe impl <T > Send for Carton <T > where Box <T >: Send {}
215+ ```
216+
217+ Right now ` Carton<T> ` has a memory leak, as it never frees the memory it allocates.
218+ Once we fix that we have a new requirement we have to ensure we meet to be Send:
219+ we need to know ` free ` can be called on a pointer that was yielded by an
220+ allocation done on another thread. We can check this is true in the docs for
221+ [ ` libc::free ` ] [ libc-free-docs ] .
222+
223+ ``` rust
224+ # struct Carton <T >(std :: ptr :: NonNull <T >);
225+ # mod libc {
226+ # pub use :: std :: os :: raw :: c_void;
227+ # extern " C" { pub fn free (p : * mut c_void ); }
228+ # }
229+ impl <T > Drop for Carton <T > {
230+ fn drop (& mut self ) {
231+ unsafe {
232+ libc :: free (self . 0. as_ptr (). cast ());
233+ }
234+ }
235+ }
236+ ```
237+
238+ A nice example where this does not happen is with a MutexGuard: notice how
239+ [ it is not Send] [ mutex-guard-not-send-docs-rs ] . The implementation of MutexGuard
240+ [ uses libraries] [ mutex-guard-not-send-comment ] that require you to ensure you
241+ don't try to free a lock that you acquired in a different thread. If you were
242+ able to Send a MutexGuard to another thread the destructor would run in the
243+ thread you sent it to, violating the requirement. MutexGuard can still be Sync
244+ because all you can send to another thread is an ` &MutexGuard ` and dropping a
245+ reference does nothing.
246+
77247TODO: better explain what can or can't be Send or Sync. Sufficient to appeal
78248only to data races?
79249
80250[ unsafe traits ] : safe-unsafe-meaning.html
251+ [ box-doc ] : https://doc.rust-lang.org/std/boxed/struct.Box.html
252+ [ box-is-special ] : https://manishearth.github.io/blog/2017/01/10/rust-tidbits-box-is-special/
253+ [ deref-doc ] : https://doc.rust-lang.org/core/ops/trait.Deref.html
254+ [ deref-mut-doc ] : https://doc.rust-lang.org/core/ops/trait.DerefMut.html
255+ [ mutex-guard-not-send-docs-rs ] : https://doc.rust-lang.org/std/sync/struct.MutexGuard.html#impl-Send
256+ [ mutex-guard-not-send-comment ] : https://github.com/rust-lang/rust/issues/23465#issuecomment-82730326
257+ [ libc-free-docs ] : https://linux.die.net/man/3/free
0 commit comments