Skip to main content

compio_send_wrapper/
lib.rs

1// Copyright 2017 Thomas Keh.
2// Copyright 2024 compio-rs
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10//! This [Rust] library implements a wrapper type called [`SendWrapper`] which
11//! allows you to move around non-[`Send`] types between threads, as long as you
12//! access the contained value only from within the original thread. You also
13//! have to make sure that the wrapper is dropped from within the original
14//! thread. If any of these constraints is violated, a panic occurs.
15//! [`SendWrapper<T>`] implements [`Send`] and [`Sync`] for any type `T`.
16//!
17//! # Examples
18//!
19//! ```rust
20//! use std::{rc::Rc, sync::mpsc::channel, thread};
21//!
22//! use compio_send_wrapper::SendWrapper;
23//!
24//! // Rc is a non-Send type.
25//! let value = Rc::new(42);
26//!
27//! // We now wrap the value with `SendWrapper` (value is moved inside).
28//! let wrapped_value = SendWrapper::new(value);
29//!
30//! // A channel allows us to move the wrapped value between threads.
31//! let (sender, receiver) = channel();
32//!
33//! let t = thread::spawn(move || {
34//!     // This would panic (because of accessing in the wrong thread):
35//!     // let value = wrapped_value.get().unwrap();
36//!
37//!     // Move SendWrapper back to main thread, so it can be dropped from there.
38//!     // If you leave this out the thread will panic because of dropping from wrong thread.
39//!     sender.send(wrapped_value).unwrap();
40//! });
41//!
42//! let wrapped_value = receiver.recv().unwrap();
43//!
44//! // Now you can use the value again.
45//! let value = wrapped_value.get().unwrap();
46//!
47//! let mut wrapped_value = wrapped_value;
48//!
49//! // You can also get a mutable reference to the value.
50//! let value = wrapped_value.get_mut().unwrap();
51//! ```
52//!
53//! # Features
54//!
55//! This crate exposes several optional features:
56//!
57//! - `futures`: Enables [`Future`] and [`Stream`] implementations for
58//!   [`SendWrapper`].
59//! - `current_thread_id`: Uses the unstable [`std::thread::current_id`] API (on
60//!   nightly Rust) to track the originating thread more efficiently.
61//! - `nightly`: Enables nightly-only, experimental functionality used by this
62//!   crate (including support for `current_thread_id` as configured in
63//!   `Cargo.toml`).
64//!
65//! You can enable them in `Cargo.toml` like so:
66//!
67//! ```toml
68//! compio-send-wrapper = { version = "...", features = ["futures"] }
69//! # or, for example:
70//! # compio-send-wrapper = { version = "...", features = ["futures", "current_thread_id"] }
71//! ```
72//!
73//! # License
74//!
75//! `compio-send-wrapper` is distributed under the terms of both the MIT license
76//! and the Apache License (Version 2.0).
77//!
78//! See LICENSE-APACHE.txt, and LICENSE-MIT.txt for details.
79//!
80//! [Rust]: https://www.rust-lang.org
81//! [`Future`]: std::future::Future
82//! [`Stream`]: futures_core::Stream
83// To build docs locally use `RUSTDOCFLAGS="--cfg docsrs" cargo doc --open --all-features`
84#![cfg_attr(docsrs, feature(doc_cfg))]
85#![cfg_attr(
86    all(not(loom), feature = "current_thread_id"),
87    feature(current_thread_id)
88)]
89#![warn(missing_docs)]
90
91#[cfg(feature = "futures")]
92#[cfg_attr(docsrs, doc(cfg(feature = "futures")))]
93mod futures;
94
95use std::{
96    fmt,
97    mem::{self, ManuallyDrop},
98    pin::Pin,
99};
100
101cfg_if::cfg_if! {
102    if #[cfg(any(loom, not(feature = "current_thread_id")))] {
103        #[cfg(loom)]
104        use loom::{thread_local, cell::Cell, thread::{self, ThreadId}};
105        #[cfg(not(loom))]
106        use std::{thread_local, cell::Cell, thread::{self, ThreadId}};
107
108        thread_local! {
109            static THREAD_ID: Cell<ThreadId> = Cell::new(thread::current().id());
110        }
111
112        /// Get the current [`ThreadId`].
113        pub(crate) fn current_id() -> ThreadId {
114            THREAD_ID.with(|id| id.get())
115        }
116    } else {
117        use std::thread::{self, current_id, ThreadId};
118    }
119}
120
121/// A wrapper which allows you to move around non-[`Send`]-types between
122/// threads, as long as you access the contained value only from within the
123/// original thread and make sure that it is dropped from within the original
124/// thread.
125pub struct SendWrapper<T: ?Sized> {
126    thread_id: ThreadId,
127    data: ManuallyDrop<T>,
128}
129
130impl<T> SendWrapper<T> {
131    /// Create a `SendWrapper<T>` wrapper around a value of type `T`.
132    /// The wrapper takes ownership of the value.
133    #[inline]
134    pub fn new(data: T) -> SendWrapper<T> {
135        SendWrapper {
136            data: ManuallyDrop::new(data),
137            thread_id: current_id(),
138        }
139    }
140
141    /// Takes the value out of the `SendWrapper<T>`.
142    ///
143    /// # Safety
144    ///
145    /// The caller should be in the same thread as the creator.
146    pub unsafe fn take_unchecked(self) -> T {
147        // Prevent drop() from being called, as it would drop `self.data` twice
148        let mut this = ManuallyDrop::new(self);
149
150        // Safety:
151        // - The caller of this unsafe function guarantees that it's valid to access `T`
152        //   from the current thread (the safe `take` method enforces this precondition
153        //   before calling `take_unchecked`).
154        // - We only move out from `self.data` here and in drop, so `self.data` is
155        //   present
156        unsafe { ManuallyDrop::take(&mut this.data) }
157    }
158
159    /// Takes the value out of the `SendWrapper<T>`.
160    ///
161    /// # Panics
162    ///
163    /// Panics if it is called from a different thread than the one the
164    /// `SendWrapper<T>` instance has been created with.
165    #[track_caller]
166    pub fn take(self) -> T {
167        if self.valid() {
168            // SAFETY: the same thread as the creator
169            unsafe { self.take_unchecked() }
170        } else {
171            invalid_deref()
172        }
173    }
174}
175
176impl<T: ?Sized> SendWrapper<T> {
177    /// Returns `true` if the value can be safely accessed from within the
178    /// current thread.
179    #[inline]
180    pub fn valid(&self) -> bool {
181        self.thread_id == current_id()
182    }
183
184    /// Returns a reference to the contained value.
185    ///
186    /// # Safety
187    ///
188    /// The caller should be in the same thread as the creator.
189    #[inline]
190    pub unsafe fn get_unchecked(&self) -> &T {
191        &self.data
192    }
193
194    /// Returns a mutable reference to the contained value.
195    ///
196    /// # Safety
197    ///
198    /// The caller should be in the same thread as the creator.
199    #[inline]
200    pub unsafe fn get_unchecked_mut(&mut self) -> &mut T {
201        &mut self.data
202    }
203
204    /// Returns a pinned reference to the contained value.
205    ///
206    /// # Safety
207    ///
208    /// The caller should be in the same thread as the creator.
209    #[inline]
210    pub unsafe fn get_unchecked_pinned(self: Pin<&Self>) -> Pin<&T> {
211        // SAFETY: as long as `SendWrapper` is pinned, the inner data is pinned too.
212        unsafe { self.map_unchecked(|s| &*s.data) }
213    }
214
215    /// Returns a pinned mutable reference to the contained value.
216    ///
217    /// # Safety
218    ///
219    /// The caller should be in the same thread as the creator.
220    #[inline]
221    pub unsafe fn get_unchecked_pinned_mut(self: Pin<&mut Self>) -> Pin<&mut T> {
222        // SAFETY: as long as `SendWrapper` is pinned, the inner data is pinned too.
223        unsafe { self.map_unchecked_mut(|s| &mut *s.data) }
224    }
225
226    /// Returns a reference to the contained value, if valid.
227    #[inline]
228    pub fn get(&self) -> Option<&T> {
229        if self.valid() { Some(&self.data) } else { None }
230    }
231
232    /// Returns a mutable reference to the contained value, if valid.
233    #[inline]
234    pub fn get_mut(&mut self) -> Option<&mut T> {
235        if self.valid() {
236            Some(&mut self.data)
237        } else {
238            None
239        }
240    }
241
242    /// Returns a pinned reference to the contained value, if valid.
243    #[inline]
244    pub fn get_pinned(self: Pin<&Self>) -> Option<Pin<&T>> {
245        if self.valid() {
246            // SAFETY: the same thread as the creator
247            Some(unsafe { self.get_unchecked_pinned() })
248        } else {
249            None
250        }
251    }
252
253    /// Returns a pinned mutable reference to the contained value, if valid.
254    #[inline]
255    pub fn get_pinned_mut(self: Pin<&mut Self>) -> Option<Pin<&mut T>> {
256        if self.valid() {
257            // SAFETY: the same thread as the creator
258            Some(unsafe { self.get_unchecked_pinned_mut() })
259        } else {
260            None
261        }
262    }
263
264    /// Returns a tracker that can be used to check if the current thread is
265    /// the same as the creator thread.
266    #[inline]
267    pub fn tracker(&self) -> SendWrapper<()> {
268        SendWrapper {
269            data: ManuallyDrop::new(()),
270            thread_id: self.thread_id,
271        }
272    }
273}
274
275unsafe impl<T: ?Sized> Send for SendWrapper<T> {}
276unsafe impl<T: ?Sized> Sync for SendWrapper<T> {}
277
278impl<T: ?Sized> Drop for SendWrapper<T> {
279    /// Drops the contained value.
280    ///
281    /// # Panics
282    ///
283    /// Dropping panics if it is done from a different thread than the one the
284    /// `SendWrapper<T>` instance has been created with.
285    ///
286    /// Exceptions:
287    /// - There is no extra panic if the thread is already panicking/unwinding.
288    ///   This is because otherwise there would be double panics (usually
289    ///   resulting in an abort) when dereferencing from a wrong thread.
290    /// - If `T` has a trivial drop ([`needs_drop::<T>()`] is false) then this
291    ///   method never panics.
292    ///
293    /// [`needs_drop::<T>()`]: std::mem::needs_drop
294    #[track_caller]
295    fn drop(&mut self) {
296        // If the drop is trivial (`needs_drop` = false), then dropping `T` can't access
297        // it and so it can be safely dropped on any thread.
298        if !mem::needs_drop::<T>() || self.valid() {
299            unsafe {
300                // Drop the inner value
301                //
302                // SAFETY:
303                // - We've just checked that it's valid to drop `T` on this thread
304                // - We only move out from `self.data` here and in drop, so `self.data` is
305                //   present
306                ManuallyDrop::drop(&mut self.data);
307            }
308        } else {
309            invalid_drop()
310        }
311    }
312}
313
314impl<T: fmt::Debug + ?Sized> fmt::Debug for SendWrapper<T> {
315    /// Formats the value using the given formatter.
316    ///
317    /// If the `SendWrapper<T>` is formatted from a different thread than the
318    /// one it was created on, the `data` field is shown as `"<invalid>"`
319    /// instead of causing a panic.
320    #[track_caller]
321    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
322        let mut f = f.debug_struct("SendWrapper");
323        if let Some(data) = self.get() {
324            f.field("data", &data);
325        } else {
326            f.field("data", &"<invalid>");
327        }
328        f.field("thread_id", &self.thread_id).finish()
329    }
330}
331
332impl<T: Clone> Clone for SendWrapper<T> {
333    /// Returns a copy of the value.
334    ///
335    /// # Panics
336    ///
337    /// Cloning panics if it is done from a different thread than the one
338    /// the `SendWrapper<T>` instance has been created with.
339    #[track_caller]
340    fn clone(&self) -> Self {
341        Self::new(self.get().unwrap_or_else(|| invalid_deref()).clone())
342    }
343}
344
345#[cold]
346#[inline(never)]
347#[track_caller]
348fn invalid_deref() -> ! {
349    const DEREF_ERROR: &str = "Accessed SendWrapper<T> variable from a thread different to the \
350                               one it has been created with.";
351
352    panic!("{}", DEREF_ERROR)
353}
354
355#[cold]
356#[inline(never)]
357#[track_caller]
358#[cfg(feature = "futures")]
359fn invalid_poll() -> ! {
360    const POLL_ERROR: &str = "Polling SendWrapper<T> variable from a thread different to the one \
361                              it has been created with.";
362
363    panic!("{}", POLL_ERROR)
364}
365
366#[cold]
367#[inline(never)]
368#[track_caller]
369fn invalid_drop() {
370    const DROP_ERROR: &str = "Dropped SendWrapper<T> variable from a thread different to the one \
371                              it has been created with.";
372
373    if !thread::panicking() {
374        // panic because of dropping from wrong thread
375        // only do this while not unwinding (could be caused by deref from wrong thread)
376        panic!("{}", DROP_ERROR)
377    }
378}
379
380#[cfg(test)]
381mod tests {
382    use std::{
383        pin::Pin,
384        rc::Rc,
385        sync::{Arc, mpsc::channel},
386        thread,
387    };
388
389    use super::SendWrapper;
390
391    #[test]
392    fn get_and_get_mut_on_creator_thread_and_pinned_variants() {
393        let mut wrapper = SendWrapper::new(1_i32);
394
395        // On the creator thread, the plain accessors should return Some.
396        let r = wrapper.get();
397        assert!(r.is_some());
398        assert_eq!(*r.unwrap(), 1);
399
400        let r_mut = wrapper.get_mut();
401        assert!(r_mut.is_some());
402        *r_mut.unwrap() = 2;
403
404        // The change via get_mut should be visible via get as well.
405        let r_after = wrapper.get();
406        assert!(r_after.is_some());
407        assert_eq!(*r_after.unwrap(), 2);
408
409        // Pinned shared reference should also succeed on the creator thread.
410        let pinned = Pin::new(&wrapper);
411        let pinned_ref = pinned.get_pinned();
412        assert!(pinned_ref.is_some());
413        assert_eq!(*pinned_ref.unwrap(), 2);
414
415        // Pinned mutable reference should succeed and allow mutation.
416        let mut wrapper2 = SendWrapper::new(10_i32);
417        let pinned_mut = Pin::new(&mut wrapper2);
418        let pinned_mut_ref = pinned_mut.get_pinned_mut();
419        assert!(pinned_mut_ref.is_some());
420        *pinned_mut_ref.unwrap() = 11;
421
422        let after_mut = wrapper2.get();
423        assert!(after_mut.is_some());
424        assert_eq!(*after_mut.unwrap(), 11);
425    }
426
427    #[test]
428    fn accessors_return_none_on_non_creator_thread() {
429        let mut wrapper = SendWrapper::new(123_i32);
430
431        // Move the wrapper to another thread; that thread is not the creator.
432        let handle = thread::spawn(move || {
433            // Plain accessors should return None on non-creator thread.
434            assert!(wrapper.get().is_none());
435            assert!(wrapper.get_mut().is_none());
436
437            // Pinned accessors should also return None on non-creator thread.
438            let pinned = Pin::new(&wrapper);
439            assert!(pinned.get_pinned().is_none());
440
441            let mut wrapper = wrapper;
442            let pinned_mut = Pin::new(&mut wrapper);
443            assert!(pinned_mut.get_pinned_mut().is_none());
444        });
445
446        handle.join().unwrap();
447    }
448
449    #[test]
450    fn test_valid() {
451        let (sender, receiver) = channel();
452        let w = SendWrapper::new(Rc::new(42));
453        assert!(w.valid());
454        let t = thread::spawn(move || {
455            // move SendWrapper back to main thread, so it can be dropped from there
456            sender.send(w).unwrap();
457        });
458        let w2 = receiver.recv().unwrap();
459        assert!(w2.valid());
460        assert!(t.join().is_ok());
461    }
462
463    #[test]
464    fn test_invalid() {
465        let w = SendWrapper::new(Rc::new(42));
466        let t = thread::spawn(move || {
467            assert!(!w.valid());
468            w
469        });
470        let join_result = t.join();
471        assert!(join_result.is_ok());
472    }
473
474    #[test]
475    fn test_drop_panic() {
476        let w = SendWrapper::new(Rc::new(42));
477        let t = thread::spawn(move || {
478            drop(w);
479        });
480        let join_result = t.join();
481        assert!(join_result.is_err());
482    }
483
484    #[test]
485    fn test_take() {
486        let w = SendWrapper::new(Rc::new(42));
487        let inner: Rc<usize> = w.take();
488        assert_eq!(42, *inner);
489    }
490
491    #[test]
492    fn test_take_panic() {
493        let w = SendWrapper::new(Rc::new(42));
494        let t = thread::spawn(move || {
495            let _ = w.take();
496        });
497        assert!(t.join().is_err());
498    }
499    #[test]
500    fn test_sync() {
501        // Arc<T> can only be sent to another thread if T Sync
502        let arc = Arc::new(SendWrapper::new(42));
503        thread::spawn(move || {
504            let _ = arc;
505        });
506    }
507
508    #[test]
509    fn test_debug() {
510        let w = SendWrapper::new(Rc::new(42));
511        let info = format!("{:?}", w);
512        assert!(info.contains("SendWrapper {"));
513        assert!(info.contains("data: 42,"));
514        assert!(info.contains("thread_id: ThreadId("));
515    }
516
517    #[test]
518    fn test_debug_invalid() {
519        let w = SendWrapper::new(Rc::new(42));
520        let t = thread::spawn(move || {
521            let info = format!("{:?}", w);
522            assert!(info.contains("SendWrapper {"));
523            assert!(info.contains("data: \"<invalid>\","));
524            assert!(info.contains("thread_id: ThreadId("));
525            w
526        });
527        assert!(t.join().is_ok());
528    }
529
530    #[test]
531    fn test_clone() {
532        let w1 = SendWrapper::new(Rc::new(42));
533        let w2 = w1.clone();
534        assert_eq!(format!("{:?}", w1), format!("{:?}", w2));
535    }
536
537    #[test]
538    fn test_clone_panic() {
539        let w = SendWrapper::new(Rc::new(42));
540        let t = thread::spawn(move || {
541            let _ = w.clone();
542        });
543        assert!(t.join().is_err());
544    }
545}