1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
// // Copyright 2017 yvt, all rights reserved. // // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your // option. This file may not be copied, modified, or distributed // except according to those terms. // //! Provides a `Send`-able cell type whose contents can be accessed only via an //! inforgeable token. //! //! # Examples //! //! ``` //! # use tokenlock::*; //! let mut token = Token::new(); //! //! let lock = TokenLock::new(&token, 1); //! assert_eq!(*lock.read(&token).unwrap(), 1); //! //! let mut guard = lock.write(&mut token).unwrap(); //! assert_eq!(*guard, 1); //! *guard = 2; //! ``` //! //! `TokenLock` implements `Send` and `Sync` so it can be shared between threads, //! but only the thread holding the original `Token` can access its contents. //! `Token` cannot be cloned: //! //! ``` //! # use tokenlock::*; //! # use std::thread; //! # use std::sync::Arc; //! # let mut token = Token::new(); //! let lock = Arc::new(TokenLock::new(&token, 1)); //! //! let lock_1 = Arc::clone(&lock); //! thread::Builder::new().spawn(move || { //! let lock_1 = lock_1; //! let mut token_1 = token; //! //! // I have `Token` so I can get a mutable reference to the contents //! lock_1.write(&mut token_1).unwrap(); //! }).unwrap(); //! //! // can't access the contents; I no longer have `Token` //! // lock.write(&mut token).unwrap(); //! ``` //! //! The lifetime of the returned reference is limited by both of the `TokenLock` //! and `Token`. //! //! ```compile_fail //! # use tokenlock::*; //! # use std::mem::drop; //! let mut token = Token::new(); //! let lock = TokenLock::new(&token, 1); //! let guard = lock.write(&mut token).unwrap(); //! drop(lock); // compile error: `guard` cannot outlive `TokenLock` //! ``` //! //! ```compile_fail //! # use tokenlock::*; //! # use std::mem::drop; //! # let mut token = Token::new(); //! # let lock = TokenLock::new(&token, 1); //! # let guard = lock.write(&mut token).unwrap(); //! drop(token); // compile error: `guard` cannot outlive `Token` //! ``` //! //! It also prevents from forming a reference to the contained value when //! there already is a mutable reference to it: //! //! ```compile_fail //! # use tokenlock::*; //! # let mut token = Token::new(); //! # let lock = TokenLock::new(&token, 1); //! let write_guard = lock.write(&mut token).unwrap(); //! let read_guard = lock.read(&token).unwrap(); // compile error //! ``` //! //! While allowing multiple immutable references: //! //! ``` //! # use tokenlock::*; //! # let mut token = Token::new(); //! # let lock = TokenLock::new(&token, 1); //! let read_guard1 = lock.read(&token).unwrap(); //! let read_guard2 = lock.read(&token).unwrap(); //! ``` use std::{mem, fmt}; use std::cell::UnsafeCell; use std::sync::Arc; /// An inforgeable token used to access the contents of a `TokenLock`. /// /// This type is not `Clone` to ensure an exclusive access to `TokenLock`. /// /// See the [module-level documentation] for more details. /// /// [module-level documentation]: index.html #[derive(Debug, PartialEq, Eq, Hash)] pub struct Token(UniqueId); unsafe impl Send for Token {} unsafe impl Sync for Token {} impl Token { pub fn new() -> Self { Token(UniqueId::new()) } } /// Token that cannot be used to access the contents of a `TokenLock`, but can /// be used to create a new `TokenLock`. /// /// See the [module-level documentation] for more details. /// /// [module-level documentation]: index.html /// /// # Examples /// /// The parameter of `TokenLock::new` accepts `Into<TokenRef>`, so the following /// codes are equivalent: /// /// ``` /// # use tokenlock::*; /// # let mut token = Token::new(); /// TokenLock::new(&token, 1); /// TokenLock::new(TokenRef::from(&token), 1); /// ``` /// /// `TokenRef` can be cloned while `Token` cannot: /// /// ``` /// # use tokenlock::*; /// let mut token = Token::new(); /// let token_ref = TokenRef::from(&token); /// let lock1 = TokenLock::new(token_ref.clone(), 1); /// let lock2 = TokenLock::new(token_ref.clone(), 2); /// ``` /// #[derive(Debug, PartialEq, Eq, Clone, Hash)] pub struct TokenRef(UniqueId); impl<'a> From<&'a Token> for TokenRef { fn from(x: &'a Token) -> TokenRef { TokenRef(x.0.clone()) } } /// A mutual exclusive primitive that can be accessed using a `Token` /// with a very low over-head. /// /// See the [module-level documentation] for more details. /// /// [module-level documentation]: index.html pub struct TokenLock<T: ?Sized> { keyhole: UniqueId, data: UnsafeCell<T>, } unsafe impl<T: ?Sized + Send + Sync> Send for TokenLock<T> {} unsafe impl<T: ?Sized + Send + Sync> Sync for TokenLock<T> {} impl<T: ?Sized> fmt::Debug for TokenLock<T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("TokenLock") .field("keyhole", &self.keyhole) .finish() } } impl<T> TokenLock<T> { pub fn new<S: Into<TokenRef>>(token: S, data: T) -> Self { Self { keyhole: token.into().0, data: UnsafeCell::new(data), } } } impl<T: ?Sized> TokenLock<T> { #[inline] #[allow(dead_code)] pub fn get_mut(&mut self) -> &mut T { unsafe { mem::transmute(self.data.get()) } } #[inline] #[allow(dead_code)] pub fn read<'a>(&'a self, token: &'a Token) -> Option<&'a T> { if token.0 == self.keyhole { Some(unsafe { &*self.data.get() }) } else { None } } #[inline] pub fn write<'a>(&'a self, token: &'a mut Token) -> Option<&'a mut T> { if token.0 == self.keyhole { Some(unsafe { &mut *self.data.get() }) } else { None } } } #[derive(Debug, Clone, Hash)] struct UniqueId(Arc<usize>); impl PartialEq for UniqueId { fn eq(&self, other: &Self) -> bool { Arc::ptr_eq(&self.0, &other.0) } } impl Eq for UniqueId {} impl UniqueId { pub fn new() -> Self { // This guarantees consistent hash generation even if Rust would // implement a moving GC in future let mut arc = Arc::new(0); let id = &*arc as *const usize as usize; *Arc::get_mut(&mut arc).unwrap() = id; UniqueId(arc) } } #[test] fn basic() { let mut token = Token::new(); let lock = TokenLock::new(&token, 1); assert_eq!(*lock.read(&token).unwrap(), 1); let guard = lock.write(&mut token).unwrap(); assert_eq!(*guard, 1); } #[test] fn bad_token() { let token1 = Token::new(); let mut token2 = Token::new(); let lock = TokenLock::new(&token1, 1); assert!(lock.write(&mut token2).is_none()); }