glib/send_unique.rs
1// Copyright 2018, The Gtk-rs Project Developers.
2// See the COPYRIGHT file at the top-level directory of this distribution.
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use std::cell::RefCell;
6use std::ops;
7
8/// Like `Send` but only if we have the unique reference to the object
9///
10/// Note that implementing this trait has to be done especially careful.
11/// It must only be implemented on types where the uniqueness of a reference
12/// can be determined, i.e. the reference count field is accessible and it
13/// must only have references itself to other types that are `Send`.
14/// `SendUnique` is *not* enough for the other types unless uniqueness of
15/// all of them can be guaranteed, which is e.g. not the case if there's a
16/// getter for them.
17pub unsafe trait SendUnique: 'static {
18 fn is_unique(&self) -> bool;
19}
20
21/// Allows sending reference counted objects that don't implement `Send` to other threads
22/// as long as only a single reference to the object exists.
23#[derive(Debug)]
24pub struct SendUniqueCell<T: SendUnique> {
25 obj: T,
26 // Thread id and refcount
27 thread: RefCell<Option<(usize, usize)>>,
28}
29
30unsafe impl<T: SendUnique> Send for SendUniqueCell<T> {}
31
32#[derive(Debug)]
33pub struct BorrowError;
34
35impl<T: SendUnique> SendUniqueCell<T> {
36 /// Create a new `SendUniqueCell` out of `obj`
37 ///
38 /// Fails if `obj` is not unique at this time
39 pub fn new(obj: T) -> Result<Self, T> {
40 if !obj.is_unique() {
41 return Err(obj);
42 }
43
44 Ok(SendUniqueCell {
45 obj,
46 thread: RefCell::new(None),
47 })
48 }
49
50 /// Borrow the contained object or panic if borrowing
51 /// is not possible at this time
52 pub fn borrow(&self) -> Ref<T> {
53 #[allow(clippy::match_wild_err_arm)]
54 match self.try_borrow() {
55 Err(_) => panic!("Can't borrow"),
56 Ok(r) => r,
57 }
58 }
59
60 /// Try borrowing the contained object
61 ///
62 /// Borrowing is possible as long as only a single reference
63 /// to the object exists, or it is borrowed from the same
64 /// thread currently
65 pub fn try_borrow(&self) -> Result<Ref<T>, BorrowError> {
66 let mut thread = self.thread.borrow_mut();
67
68 // If the object is unique, we can borrow it from
69 // any thread we want and just have to keep track
70 // how often we borrowed it
71 if self.obj.is_unique() {
72 if *thread == None {
73 *thread = Some((::get_thread_id(), 1));
74 } else {
75 thread.as_mut().unwrap().1 += 1;
76 }
77
78 return Ok(Ref(self));
79 }
80
81 // If we don't even know from which thread it is borrowed, this
82 // means it somehow got borrowed from outside the SendUniqueCell
83 if *thread == None {
84 return Err(BorrowError);
85 }
86
87 // If the object is not unique, we can only borrow it
88 // from the thread that currently has it borrowed
89 if thread.as_ref().unwrap().0 != ::get_thread_id() {
90 return Err(BorrowError);
91 }
92
93 thread.as_mut().unwrap().1 += 1;
94
95 Ok(Ref(self))
96 }
97
98 /// Extract the contained object or panic if it is not possible
99 /// at this time
100 pub fn into_inner(self) -> T {
101 #[allow(clippy::match_wild_err_arm)]
102 match self.try_into_inner() {
103 Err(_) => panic!("Can't convert into inner type"),
104 Ok(obj) => obj,
105 }
106 }
107
108 /// Try extracing the contained object
109 ///
110 /// Borrowing is possible as long as only a single reference
111 /// to the object exists, or it is borrowed from the same
112 /// thread currently
113 pub fn try_into_inner(self) -> Result<T, Self> {
114 if self.try_borrow().is_err() {
115 Err(self)
116 } else {
117 Ok(self.obj)
118 }
119 }
120}
121
122pub struct Ref<'a, T: SendUnique>(&'a SendUniqueCell<T>);
123
124impl<'a, T: SendUnique> AsRef<T> for Ref<'a, T> {
125 fn as_ref(&self) -> &T {
126 &self.0.obj
127 }
128}
129
130impl<'a, T: SendUnique> ops::Deref for Ref<'a, T> {
131 type Target = T;
132
133 fn deref(&self) -> &T {
134 &self.0.obj
135 }
136}
137
138impl<'a, T: SendUnique> Drop for Ref<'a, T> {
139 fn drop(&mut self) {
140 let is_unique = self.0.obj.is_unique();
141 let mut thread = self.0.thread.borrow_mut();
142
143 if is_unique && thread.as_ref().unwrap().1 == 1 {
144 *thread = None;
145 } else {
146 thread.as_mut().unwrap().1 -= 1;
147 }
148 }
149}