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}