parity_send_wrapper/lib.rs
1// Copyright 2017 Thomas Keh.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! This [Rust] library implements a wrapper type called `SendWrapper` which allows you to move around non-[`Send`] types
10//! between threads, as long as you access the contained value only from within the original thread. You also have to
11//! make sure that the wrapper is dropped from within the original thread. If any of these constraints is violated,
12//! a panic occurs.
13//!
14//! The idea for this library was born in the context of a [`GTK+`]/[`gtk-rs`]-based application. [`GTK+`] applications
15//! are strictly single-threaded. It is not allowed to call any [`GTK+`] method from a thread different to the main
16//! thread. Consequently, all [`gtk-rs`] structs are non-[`Send`].
17//!
18//! Sometimes you still want to do some work in background. It is possible to enqueue [`GTK+`] calls from there to be
19//! executed in the main thread [using `Glib`]. This way you can know, that the [`gtk-rs`] structs involved are only
20//! accessed in the main thread and will also be dropped there. This library makes it possible that [`gtk-rs`] structs
21//! can leave the main thread at all, like required in the given
22//!
23//! # Examples
24//!
25//! ```rust
26//! use send_wrapper::SendWrapper;
27//! use std::rc::Rc;
28//! use std::thread;
29//! use std::sync::mpsc::channel;
30//!
31//! // This import is important. It allows you to unwrap the value using deref(),
32//! // deref_mut() or Deref coercion.
33//! use std::ops::{Deref, DerefMut};
34//!
35//! // Rc is a non-Send type.
36//! let value = Rc::new(42);
37//!
38//! // We now wrap the value with `SendWrapper` (value is moved inside).
39//! let wrapped_value = SendWrapper::new(value);
40//!
41//! // A channel allows us to move the wrapped value between threads.
42//! let (sender, receiver) = channel();
43//!
44//! let t = thread::spawn(move || {
45//!
46//!// This would panic (because of dereferencing in wrong thread):
47//!// let value = wrapped_value.deref();
48//!
49//! // Move SendWrapper back to main thread, so it can be dropped from there.
50//! // If you leave this out the thread will panic because of dropping from wrong thread.
51//! sender.send(wrapped_value).unwrap();
52//!
53//! });
54//!
55//! let wrapped_value = receiver.recv().unwrap();
56//!
57//! // Now you can use the value again.
58//! let value = wrapped_value.deref();
59//!
60//! // alternatives for dereferencing:
61//! // let value = *wrapped_value;
62//! // let value: &NonSendType = &wrapped_value;
63//!
64//! // alternatives for mutable dereferencing (value and wrapped_value must be mutable too, then):
65//! // let mut value = wrapped_value.deref_mut();
66//! // let mut value = &mut *wrapped_value;
67//! // let mut value: &mut NonSendType = &mut wrapped_value;
68//! ```
69//!
70//! # License
71//!
72//! `send_wrapper` is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
73//!
74//! See LICENSE-APACHE.txt, and LICENSE-MIT.txt for details.
75//!
76//! [Rust]: https://www.rust-lang.org
77//! [`Send`]: https://doc.rust-lang.org/std/marker/trait.Send.html
78//! [`gtk-rs`]: http://gtk-rs.org/
79//! [`GTK+`]: https://www.gtk.org/
80//! [using `Glib`]: http://gtk-rs.org/docs/glib/source/fn.idle_add.html
81
82use std::ops::{Drop,Deref,DerefMut};
83use std::marker::Send;
84use std::thread;
85use std::thread::ThreadId;
86
87const DEREF_ERROR: &'static str = "Dropped SendWrapper<T> variable from a thread different to the one it has been created with.";
88const DROP_ERROR: &'static str = "Dereferenced SendWrapper<T> variable from a thread different to the one it has been created with.";
89
90/// A wrapper which allows you to move around non-[`Send`]-types between threads, as long as you access the contained
91/// value only from within the original thread and make sure that it is dropped from within the original thread.
92pub struct SendWrapper<T> {
93 data: *mut T,
94 thread_id: ThreadId,
95}
96
97impl<T> SendWrapper<T> {
98
99 /// Create a SendWrapper<T> wrapper around a value of type T.
100 /// The wrapper takes ownership of the value.
101 pub fn new(data: T) -> SendWrapper<T> {
102 SendWrapper {
103 data: Box::into_raw(Box::new(data)),
104 thread_id: thread::current().id()
105 }
106 }
107
108 /// Returns if the value can be safely accessed from within the current thread.
109 pub fn valid(&self) -> bool {
110 self.thread_id == thread::current().id()
111 }
112
113 /// Takes the value out of the SendWrapper.
114 ///
115 /// #Panics
116 /// Panics if it is called from a different thread than the one the SendWrapper<T> instance has
117 /// been created with.
118 pub fn take(self) -> T {
119 if !self.valid() {
120 panic!(DEREF_ERROR);
121 }
122 let result = unsafe { Box::from_raw(self.data) };
123 // Prevent drop() from being called, as it would drop self.data twice
124 std::mem::forget(self);
125 *result
126 }
127}
128
129unsafe impl<T> Send for SendWrapper<T> { }
130unsafe impl<T> Sync for SendWrapper<T> { }
131
132impl<T> Deref for SendWrapper<T> {
133 type Target = T;
134
135 /// Returns a reference to the contained value.
136 ///
137 /// # Panics
138 /// Derefencing panics if it is done from a different thread than the one the SendWrapper<T> instance has been
139 /// created with.
140 fn deref(&self) -> &T {
141 if !self.valid() {
142 panic!(DEREF_ERROR);
143 }
144 unsafe {
145 // Access the value. We just checked that it is valid.
146 &*self.data
147 }
148 }
149}
150
151impl<T> DerefMut for SendWrapper<T> {
152
153 /// Returns a mutable reference to the contained value.
154 ///
155 /// # Panics
156 /// Derefencing panics if it is done from a different thread than the one the SendWrapper<T> instance has been
157 /// created with.
158 fn deref_mut(&mut self) -> &mut T {
159 if !self.valid() {
160 panic!(DEREF_ERROR);
161 }
162 unsafe {
163 // Access the value. We just checked that it is valid.
164 &mut *self.data
165 }
166 }
167}
168
169impl<T> Drop for SendWrapper<T> {
170
171 /// Drops the contained value.
172 ///
173 /// # Panics
174 /// Dropping panics if it is done from a different thread than the one the SendWrapper<T> instance has been
175 /// created with. As an exception, there is no extra panic if the thread is already panicking/unwinding. This is
176 /// because otherwise there would be double panics (usually resulting in an abort) when dereferencing from a wrong
177 /// thread.
178 fn drop(&mut self) {
179 if self.valid() {
180 unsafe {
181 // Create a boxed value from the raw pointer. We just checked that the pointer is valid.
182 // Box handles the dropping for us when _dropper goes out of scope.
183 let _dropper = Box::from_raw(self.data);
184 }
185 } else {
186 if !std::thread::panicking() {
187 // panic because of dropping from wrong thread
188 // only do this while not unwinding (coud be caused by deref from wrong thread)
189 panic!(DROP_ERROR);
190 }
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197
198 use SendWrapper;
199 use std::thread;
200 use std::sync::mpsc::channel;
201 use std::ops::Deref;
202 use std::rc::Rc;
203
204 #[test]
205 fn test_deref() {
206 let (sender, receiver) = channel();
207 let w = SendWrapper::new(Rc::new(42));
208 {
209 let _x = w.deref();
210 }
211 let t = thread::spawn(move || {
212 // move SendWrapper back to main thread, so it can be dropped from there
213 sender.send(w).unwrap();
214 });
215 let w2 = receiver.recv().unwrap();
216 {
217 let _x = w2.deref();
218 }
219 assert!(t.join().is_ok());
220 }
221
222 #[test]
223 fn test_deref_panic() {
224 let w = SendWrapper::new(Rc::new(42));
225 let t = thread::spawn(move || {
226 let _x = w.deref();
227 });
228 let join_result = t.join();
229 assert!(join_result.is_err());
230 }
231
232 #[test]
233 fn test_drop_panic() {
234 let w = SendWrapper::new(Rc::new(42));
235 let t = thread::spawn(move || {
236 let _x = w;
237 });
238 let join_result = t.join();
239 assert!(join_result.is_err());
240 }
241
242 #[test]
243 fn test_valid() {
244 let w = SendWrapper::new(Rc::new(42));
245 assert!(w.valid());
246 thread::spawn(move || {
247 assert!(!w.valid());
248 });
249 }
250
251 #[test]
252 fn test_take() {
253 let w = SendWrapper::new(Rc::new(42));
254 let inner: Rc<usize> = w.take();
255 assert_eq!(42, *inner);
256 }
257
258 #[test]
259 fn test_take_panic() {
260 let w = SendWrapper::new(Rc::new(42));
261 let t = thread::spawn(move || {
262 let _ = w.take();
263 });
264 assert!(t.join().is_err());
265 }
266
267}