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}