shared_lock.rs (12003B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 //! Different objects protected by the same lock 6 7 use crate::stylesheets::Origin; 8 #[cfg(feature = "gecko")] 9 use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; 10 #[cfg(feature = "servo")] 11 use parking_lot::RwLock; 12 use servo_arc::Arc; 13 use std::cell::UnsafeCell; 14 use std::fmt; 15 #[cfg(feature = "servo")] 16 use std::mem; 17 #[cfg(feature = "gecko")] 18 use std::ptr; 19 use style_traits::{CssString, CssStringWriter}; 20 use to_shmem::{SharedMemoryBuilder, ToShmem}; 21 22 /// A shared read/write lock that can protect multiple objects. 23 /// 24 /// In Gecko builds, we don't need the blocking behavior, just the safety. As 25 /// such we implement this with an AtomicRefCell instead in Gecko builds, 26 /// which is ~2x as fast, and panics (rather than deadlocking) when things go 27 /// wrong (which is much easier to debug on CI). 28 /// 29 /// Servo needs the blocking behavior for its unsynchronized animation setup, 30 /// but that may not be web-compatible and may need to be changed (at which 31 /// point Servo could use AtomicRefCell too). 32 /// 33 /// Gecko also needs the ability to have "read only" SharedRwLocks, which are 34 /// used for objects stored in (read only) shared memory. Attempting to acquire 35 /// write access to objects protected by a read only SharedRwLock will panic. 36 #[derive(Clone)] 37 #[cfg_attr(feature = "servo", derive(crate::derives::MallocSizeOf))] 38 pub struct SharedRwLock { 39 #[cfg(feature = "servo")] 40 #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] 41 arc: Arc<RwLock<()>>, 42 43 #[cfg(feature = "gecko")] 44 cell: Option<Arc<AtomicRefCell<SomethingZeroSizedButTyped>>>, 45 } 46 47 #[cfg(feature = "gecko")] 48 struct SomethingZeroSizedButTyped; 49 50 impl fmt::Debug for SharedRwLock { 51 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 52 f.write_str("SharedRwLock") 53 } 54 } 55 56 impl SharedRwLock { 57 /// Create a new shared lock (servo). 58 #[cfg(feature = "servo")] 59 pub fn new() -> Self { 60 SharedRwLock { 61 arc: Arc::new(RwLock::new(())), 62 } 63 } 64 65 /// Create a new shared lock (gecko). 66 #[cfg(feature = "gecko")] 67 pub fn new() -> Self { 68 SharedRwLock { 69 cell: Some(Arc::new(AtomicRefCell::new(SomethingZeroSizedButTyped))), 70 } 71 } 72 73 /// Create a new global shared lock (servo). 74 #[cfg(feature = "servo")] 75 pub fn new_leaked() -> Self { 76 SharedRwLock { 77 arc: Arc::new_leaked(RwLock::new(())), 78 } 79 } 80 81 /// Create a new global shared lock (gecko). 82 #[cfg(feature = "gecko")] 83 pub fn new_leaked() -> Self { 84 SharedRwLock { 85 cell: Some(Arc::new_leaked(AtomicRefCell::new( 86 SomethingZeroSizedButTyped, 87 ))), 88 } 89 } 90 91 /// Create a new read-only shared lock (gecko). 92 #[cfg(feature = "gecko")] 93 pub fn read_only() -> Self { 94 SharedRwLock { cell: None } 95 } 96 97 #[cfg(feature = "gecko")] 98 #[inline] 99 fn ptr(&self) -> *const SomethingZeroSizedButTyped { 100 self.cell 101 .as_ref() 102 .map(|cell| cell.as_ptr() as *const _) 103 .unwrap_or(ptr::null()) 104 } 105 106 /// Wrap the given data to make its access protected by this lock. 107 pub fn wrap<T>(&self, data: T) -> Locked<T> { 108 Locked { 109 shared_lock: self.clone(), 110 data: UnsafeCell::new(data), 111 } 112 } 113 114 /// Obtain the lock for reading (servo). 115 #[cfg(feature = "servo")] 116 pub fn read(&self) -> SharedRwLockReadGuard<'_> { 117 mem::forget(self.arc.read()); 118 SharedRwLockReadGuard(self) 119 } 120 121 /// Obtain the lock for reading (gecko). 122 #[cfg(feature = "gecko")] 123 pub fn read(&self) -> SharedRwLockReadGuard<'_> { 124 SharedRwLockReadGuard(self.cell.as_ref().map(|cell| cell.borrow())) 125 } 126 127 /// Obtain the lock for writing (servo). 128 #[cfg(feature = "servo")] 129 pub fn write(&self) -> SharedRwLockWriteGuard<'_> { 130 mem::forget(self.arc.write()); 131 SharedRwLockWriteGuard(self) 132 } 133 134 /// Obtain the lock for writing (gecko). 135 #[cfg(feature = "gecko")] 136 pub fn write(&self) -> SharedRwLockWriteGuard<'_> { 137 SharedRwLockWriteGuard(self.cell.as_ref().unwrap().borrow_mut()) 138 } 139 } 140 141 /// Proof that a shared lock was obtained for reading (servo). 142 #[cfg(feature = "servo")] 143 pub struct SharedRwLockReadGuard<'a>(&'a SharedRwLock); 144 /// Proof that a shared lock was obtained for reading (gecko). 145 #[cfg(feature = "gecko")] 146 pub struct SharedRwLockReadGuard<'a>(Option<AtomicRef<'a, SomethingZeroSizedButTyped>>); 147 #[cfg(feature = "servo")] 148 impl<'a> Drop for SharedRwLockReadGuard<'a> { 149 fn drop(&mut self) { 150 // Unsafe: self.lock is private to this module, only ever set after `read()`, 151 // and never copied or cloned (see `compile_time_assert` below). 152 unsafe { self.0.arc.force_unlock_read() } 153 } 154 } 155 156 impl<'a> SharedRwLockReadGuard<'a> { 157 #[inline] 158 #[cfg(feature = "gecko")] 159 fn ptr(&self) -> *const SomethingZeroSizedButTyped { 160 self.0 161 .as_ref() 162 .map(|r| &**r as *const _) 163 .unwrap_or(ptr::null()) 164 } 165 } 166 167 /// Proof that a shared lock was obtained for writing (servo). 168 #[cfg(feature = "servo")] 169 pub struct SharedRwLockWriteGuard<'a>(&'a SharedRwLock); 170 /// Proof that a shared lock was obtained for writing (gecko). 171 #[cfg(feature = "gecko")] 172 pub struct SharedRwLockWriteGuard<'a>(AtomicRefMut<'a, SomethingZeroSizedButTyped>); 173 #[cfg(feature = "servo")] 174 impl<'a> Drop for SharedRwLockWriteGuard<'a> { 175 fn drop(&mut self) { 176 // Unsafe: self.lock is private to this module, only ever set after `write()`, 177 // and never copied or cloned (see `compile_time_assert` below). 178 unsafe { self.0.arc.force_unlock_write() } 179 } 180 } 181 182 /// Data protect by a shared lock. 183 pub struct Locked<T> { 184 shared_lock: SharedRwLock, 185 data: UnsafeCell<T>, 186 } 187 188 // Unsafe: the data inside `UnsafeCell` is only accessed in `read_with` and `write_with`, 189 // where guards ensure synchronization. 190 unsafe impl<T: Send> Send for Locked<T> {} 191 unsafe impl<T: Send + Sync> Sync for Locked<T> {} 192 193 impl<T: fmt::Debug> fmt::Debug for Locked<T> { 194 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 195 let guard = self.shared_lock.read(); 196 self.read_with(&guard).fmt(f) 197 } 198 } 199 200 impl<T> Locked<T> { 201 #[cfg(feature = "gecko")] 202 #[inline] 203 fn is_read_only_lock(&self) -> bool { 204 self.shared_lock.cell.is_none() 205 } 206 207 #[cfg(feature = "servo")] 208 fn same_lock_as(&self, lock: &SharedRwLock) -> bool { 209 Arc::ptr_eq(&self.shared_lock.arc, &lock.arc) 210 } 211 212 #[cfg(feature = "gecko")] 213 fn same_lock_as(&self, ptr: *const SomethingZeroSizedButTyped) -> bool { 214 ptr::eq(self.shared_lock.ptr(), ptr) 215 } 216 217 /// Access the data for reading. 218 pub fn read_with<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a T { 219 #[cfg(feature = "gecko")] 220 assert!( 221 self.is_read_only_lock() || self.same_lock_as(guard.ptr()), 222 "Locked::read_with called with a guard from an unrelated SharedRwLock: {:?} vs. {:?}", 223 self.shared_lock.ptr(), 224 guard.ptr(), 225 ); 226 #[cfg(not(feature = "gecko"))] 227 assert!(self.same_lock_as(&guard.0)); 228 229 let ptr = self.data.get(); 230 231 // Unsafe: 232 // 233 // * The guard guarantees that the lock is taken for reading, 234 // and we’ve checked that it’s the correct lock. 235 // * The returned reference borrows *both* the data and the guard, 236 // so that it can outlive neither. 237 unsafe { &*ptr } 238 } 239 240 /// Access the data for reading without verifying the lock. Use with caution. 241 #[cfg(feature = "gecko")] 242 pub unsafe fn read_unchecked<'a>(&'a self) -> &'a T { 243 let ptr = self.data.get(); 244 &*ptr 245 } 246 247 /// Access the data for writing. 248 pub fn write_with<'a>(&'a self, guard: &'a mut SharedRwLockWriteGuard) -> &'a mut T { 249 #[cfg(feature = "gecko")] 250 assert!( 251 !self.is_read_only_lock() && self.same_lock_as(&*guard.0), 252 "Locked::write_with called with a guard from a read only or unrelated SharedRwLock" 253 ); 254 #[cfg(not(feature = "gecko"))] 255 assert!(self.same_lock_as(&guard.0)); 256 257 let ptr = self.data.get(); 258 259 // Unsafe: 260 // 261 // * The guard guarantees that the lock is taken for writing, 262 // and we’ve checked that it’s the correct lock. 263 // * The returned reference borrows *both* the data and the guard, 264 // so that it can outlive neither. 265 // * We require a mutable borrow of the guard, 266 // so that one write guard can only be used once at a time. 267 unsafe { &mut *ptr } 268 } 269 } 270 271 #[cfg(feature = "gecko")] 272 impl<T: ToShmem> ToShmem for Locked<T> { 273 fn to_shmem(&self, builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { 274 use std::mem::ManuallyDrop; 275 276 let guard = self.shared_lock.read(); 277 Ok(ManuallyDrop::new(Locked { 278 shared_lock: SharedRwLock::read_only(), 279 data: UnsafeCell::new(ManuallyDrop::into_inner( 280 self.read_with(&guard).to_shmem(builder)?, 281 )), 282 })) 283 } 284 } 285 286 #[cfg(feature = "servo")] 287 impl<T: ToShmem> ToShmem for Locked<T> { 288 fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result<Self> { 289 panic!("ToShmem not supported in Servo currently") 290 } 291 } 292 293 #[allow(dead_code)] 294 mod compile_time_assert { 295 use super::{SharedRwLockReadGuard, SharedRwLockWriteGuard}; 296 297 trait Marker1 {} 298 impl<T: Clone> Marker1 for T {} 299 impl<'a> Marker1 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Clone 300 impl<'a> Marker1 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Clone 301 302 trait Marker2 {} 303 impl<T: Copy> Marker2 for T {} 304 impl<'a> Marker2 for SharedRwLockReadGuard<'a> {} // Assert SharedRwLockReadGuard: !Copy 305 impl<'a> Marker2 for SharedRwLockWriteGuard<'a> {} // Assert SharedRwLockWriteGuard: !Copy 306 } 307 308 /// Like ToCss, but with a lock guard given by the caller, and with the writer specified 309 /// concretely rather than with a parameter. 310 pub trait ToCssWithGuard { 311 /// Serialize `self` in CSS syntax, writing to `dest`, using the given lock guard. 312 fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result; 313 314 /// Serialize `self` in CSS syntax using the given lock guard and return a string. 315 /// 316 /// (This is a convenience wrapper for `to_css` and probably should not be overridden.) 317 #[inline] 318 fn to_css_string(&self, guard: &SharedRwLockReadGuard) -> CssString { 319 let mut s = CssString::new(); 320 self.to_css(guard, &mut s).unwrap(); 321 s 322 } 323 } 324 325 /// A trait to do a deep clone of a given CSS type. Gets a lock and a read 326 /// guard, in order to be able to read and clone nested structures. 327 pub trait DeepCloneWithLock: Sized { 328 /// Deep clones this object. 329 fn deep_clone_with_lock(&self, lock: &SharedRwLock, guard: &SharedRwLockReadGuard) -> Self; 330 } 331 332 /// Guards for a document 333 #[derive(Clone)] 334 pub struct StylesheetGuards<'a> { 335 /// For author-origin stylesheets. 336 pub author: &'a SharedRwLockReadGuard<'a>, 337 338 /// For user-agent-origin and user-origin stylesheets 339 pub ua_or_user: &'a SharedRwLockReadGuard<'a>, 340 } 341 342 impl<'a> StylesheetGuards<'a> { 343 /// Get the guard for a given stylesheet origin. 344 pub fn for_origin(&self, origin: Origin) -> &SharedRwLockReadGuard<'a> { 345 match origin { 346 Origin::Author => &self.author, 347 _ => &self.ua_or_user, 348 } 349 } 350 351 /// Same guard for all origins 352 pub fn same(guard: &'a SharedRwLockReadGuard<'a>) -> Self { 353 StylesheetGuards { 354 author: guard, 355 ua_or_user: guard, 356 } 357 } 358 }