swc_common/
sync.rs

1// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
11//! This module defines types which are thread safe if `cfg!(feature =
12//! "concurrent")` is true.
13//!
14//! `Lrc` is an alias of either Rc or Arc.
15//!
16//! `Lock` is a mutex.
17//! It internally uses `parking_lot::Mutex` if cfg!(parallel_queries) is true,
18//! `RefCell` otherwise.
19//!
20//! `RwLock` is a read-write lock.
21//! It internally uses `parking_lot::RwLock` if cfg!(parallel_queries) is true,
22//! `RefCell` otherwise.
23//!
24//! `LockCell` is a thread safe version of `Cell`, with `set` and `get`
25//! operations. It can never deadlock. It uses `Cell` when
26//! cfg!(parallel_queries) is false, otherwise it is a `Lock`.
27//!
28//! `MTLock` is a mutex which disappears if cfg!(parallel_queries) is false.
29//!
30//! `MTRef` is a immutable reference if cfg!(parallel_queries), and an mutable
31//! reference otherwise.
32//!
33//! `rustc_erase_owner!` erases a OwningRef owner into Erased or Erased + Send +
34//! Sync depending on the value of cfg!(parallel_queries).
35
36#[cfg(not(feature = "concurrent"))]
37use std::cell::{RefCell as InnerRwLock, RefCell as InnerLock};
38use std::{
39    cmp::Ordering,
40    collections::HashMap,
41    fmt,
42    fmt::{Debug, Formatter},
43    hash::{BuildHasher, Hash},
44};
45
46#[cfg(feature = "concurrent")]
47use parking_lot::{Mutex as InnerLock, RwLock as InnerRwLock};
48
49#[cfg(feature = "concurrent")]
50pub use self::concurrent::*;
51#[cfg(not(feature = "concurrent"))]
52pub use self::single::*;
53
54#[cfg(feature = "concurrent")]
55mod concurrent {
56    pub use std::{
57        marker::{Send, Sync},
58        sync::Arc as Lrc,
59    };
60
61    pub use once_cell::sync::{Lazy, OnceCell};
62    pub use parking_lot::{
63        MappedMutexGuard as MappedLockGuard, MappedRwLockReadGuard as MappedReadGuard,
64        MappedRwLockWriteGuard as MappedWriteGuard, MutexGuard as LockGuard,
65        RwLockReadGuard as ReadGuard, RwLockWriteGuard as WriteGuard,
66    };
67}
68
69#[cfg(not(feature = "concurrent"))]
70mod single {
71    pub use once_cell::unsync::{Lazy, OnceCell};
72    /// Dummy trait because swc_common is in single thread mode.
73    pub trait Send {}
74    /// Dummy trait because swc_common is in single thread mode.
75    pub trait Sync {}
76
77    impl<T> Send for T where T: ?Sized {}
78    impl<T> Sync for T where T: ?Sized {}
79
80    pub use std::{
81        cell::{
82            Ref as ReadGuard, RefMut as WriteGuard, RefMut as MappedWriteGuard,
83            RefMut as LockGuard, RefMut as MappedLockGuard,
84        },
85        rc::{Rc as Lrc, Weak},
86    };
87}
88
89#[derive(Debug)]
90pub struct Lock<T>(InnerLock<T>);
91
92impl<T> Lock<T> {
93    #[inline(always)]
94    pub fn new(inner: T) -> Self {
95        Lock(InnerLock::new(inner))
96    }
97
98    // #[inline(always)]
99    // pub fn into_inner(self) -> T {
100    //     self.0.into_inner()
101    // }
102    //
103    // #[inline(always)]
104    // pub fn get_mut(&mut self) -> &mut T {
105    //     self.0.get_mut()
106    // }
107
108    // #[cfg(feature = "concurrent")]
109    // #[inline(always)]
110    // pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
111    //     self.0.try_lock()
112    // }
113    //
114    // #[cfg(not(feature = "concurrent"))]
115    // #[inline(always)]
116    // pub fn try_lock(&self) -> Option<LockGuard<'_, T>> {
117    //     self.0.try_borrow_mut().ok()
118    // }
119
120    #[cfg(feature = "concurrent")]
121    #[inline(always)]
122    pub fn lock(&self) -> LockGuard<'_, T> {
123        self.0.lock()
124    }
125
126    #[cfg(not(feature = "concurrent"))]
127    #[inline(always)]
128    pub fn lock(&self) -> LockGuard<'_, T> {
129        self.0.borrow_mut()
130    }
131
132    // #[inline(always)]
133    // pub fn with_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
134    //     f(&mut *self.lock())
135    // }
136
137    #[inline(always)]
138    pub fn borrow(&self) -> LockGuard<'_, T> {
139        self.lock()
140    }
141
142    #[inline(always)]
143    pub fn borrow_mut(&self) -> LockGuard<'_, T> {
144        self.lock()
145    }
146}
147
148impl<T: Default> Default for Lock<T> {
149    #[inline]
150    fn default() -> Self {
151        Lock::new(T::default())
152    }
153}
154
155impl<T> LockCell<T> {
156    #[inline(always)]
157    pub fn new(inner: T) -> Self {
158        LockCell(Lock::new(inner))
159    }
160
161    #[inline(always)]
162    pub fn set(&self, new_inner: T) {
163        *self.0.lock() = new_inner;
164    }
165
166    #[inline(always)]
167    pub fn get(&self) -> T
168    where
169        T: Copy,
170    {
171        *self.0.lock()
172    }
173}
174
175pub trait HashMapExt<K, V> {
176    /// Same as HashMap::insert, but it may panic if there's already an
177    /// entry for `key` with a value not equal to `value`
178    fn insert_same(&mut self, key: K, value: V);
179}
180
181impl<K: Eq + Hash, V: Eq, S: BuildHasher> HashMapExt<K, V> for HashMap<K, V, S> {
182    fn insert_same(&mut self, key: K, value: V) {
183        self.entry(key)
184            .and_modify(|old| assert!(*old == value))
185            .or_insert(value);
186    }
187}
188
189impl<T: Copy + Debug> Debug for LockCell<T> {
190    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
191        f.debug_struct("LockCell")
192            .field("value", &self.get())
193            .finish()
194    }
195}
196
197impl<T: Default> Default for LockCell<T> {
198    /// Creates a `LockCell<T>`, with the `Default` value for T.
199    #[inline]
200    fn default() -> LockCell<T> {
201        LockCell::new(Default::default())
202    }
203}
204
205impl<T: PartialEq + Copy> PartialEq for LockCell<T> {
206    #[inline]
207    fn eq(&self, other: &LockCell<T>) -> bool {
208        self.get() == other.get()
209    }
210}
211
212impl<T: Eq + Copy> Eq for LockCell<T> {}
213
214impl<T: PartialOrd + Copy> PartialOrd for LockCell<T> {
215    #[inline]
216    fn partial_cmp(&self, other: &LockCell<T>) -> Option<Ordering> {
217        self.get().partial_cmp(&other.get())
218    }
219
220    #[inline]
221    fn lt(&self, other: &LockCell<T>) -> bool {
222        self.get() < other.get()
223    }
224
225    #[inline]
226    fn le(&self, other: &LockCell<T>) -> bool {
227        self.get() <= other.get()
228    }
229
230    #[inline]
231    fn gt(&self, other: &LockCell<T>) -> bool {
232        self.get() > other.get()
233    }
234
235    #[inline]
236    fn ge(&self, other: &LockCell<T>) -> bool {
237        self.get() >= other.get()
238    }
239}
240
241impl<T: Ord + Copy> Ord for LockCell<T> {
242    #[inline]
243    fn cmp(&self, other: &LockCell<T>) -> Ordering {
244        self.get().cmp(&other.get())
245    }
246}
247
248#[derive(Debug, Default)]
249pub struct RwLock<T>(InnerRwLock<T>);
250
251impl<T> RwLock<T> {
252    #[inline(always)]
253    pub fn new(inner: T) -> Self {
254        RwLock(InnerRwLock::new(inner))
255    }
256
257    #[cfg(not(feature = "concurrent"))]
258    #[inline(always)]
259    pub fn read(&self) -> ReadGuard<'_, T> {
260        self.0.borrow()
261    }
262
263    #[cfg(feature = "concurrent")]
264    #[inline(always)]
265    pub fn read(&self) -> ReadGuard<'_, T> {
266        self.0.read()
267    }
268
269    #[inline(always)]
270    pub fn borrow(&self) -> ReadGuard<'_, T> {
271        self.read()
272    }
273
274    #[inline(always)]
275    pub fn get_mut(&mut self) -> &mut T {
276        self.0.get_mut()
277    }
278
279    #[inline(always)]
280    pub fn with_read_lock<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
281        f(&*self.read())
282    }
283
284    #[allow(clippy::result_unit_err)]
285    #[cfg(not(feature = "concurrent"))]
286    #[inline(always)]
287    pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> {
288        self.0.try_borrow_mut().map_err(|_| ())
289    }
290
291    #[allow(clippy::result_unit_err)]
292    #[cfg(feature = "concurrent")]
293    #[inline(always)]
294    pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> {
295        self.0.try_write().ok_or(())
296    }
297
298    #[cfg(not(feature = "concurrent"))]
299    #[inline(always)]
300    pub fn write(&self) -> WriteGuard<'_, T> {
301        self.0.borrow_mut()
302    }
303
304    #[cfg(feature = "concurrent")]
305    #[inline(always)]
306    pub fn write(&self) -> WriteGuard<'_, T> {
307        self.0.write()
308    }
309
310    #[inline(always)]
311    pub fn with_write_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
312        f(&mut *self.write())
313    }
314
315    #[inline(always)]
316    pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
317        self.write()
318    }
319}
320
321// FIXME: Probably a bad idea
322impl<T: Clone> Clone for RwLock<T> {
323    #[inline]
324    fn clone(&self) -> Self {
325        RwLock::new(self.borrow().clone())
326    }
327}
328
329pub struct LockCell<T>(Lock<T>);