Source code

Revision control

Other Tools

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