Skip to main content

netstack3_sync/
lib.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! Synchronization primitives for Netstack3.
6
7#![warn(missing_docs, unreachable_patterns, unused)]
8
9extern crate alloc;
10
11#[cfg(loom)]
12pub(crate) use loom::sync;
13#[cfg(not(loom))]
14pub(crate) use std::sync;
15
16use net_types::ip::{GenericOverIp, Ip};
17
18// Re-export atomics module for loom compatibility in appropriate targets.
19pub use sync::atomic;
20
21pub mod rc;
22
23/// A mutex assuming lock poisoning will never occur.
24#[derive(Debug, Default)]
25pub struct Mutex<T>(
26    #[cfg(all(target_os = "fuchsia", not(loom)))] fuchsia_sync::Mutex<T>,
27    #[cfg(not(all(target_os = "fuchsia", not(loom))))] sync::Mutex<T>,
28);
29
30/// Lock guard for access to a [`Mutex`].
31#[cfg(all(target_os = "fuchsia", not(loom)))]
32pub type LockGuard<'a, T> = fuchsia_sync::MutexGuard<'a, T>;
33
34/// Lock guard for access to a [`Mutex`].
35#[cfg(not(all(target_os = "fuchsia", not(loom))))]
36pub type LockGuard<'a, T> = sync::MutexGuard<'a, T>;
37
38impl<T> Mutex<T> {
39    /// Creates a new mutex in an unlocked state ready for use.
40    #[inline]
41    pub fn new(t: T) -> Mutex<T> {
42        #[cfg(all(target_os = "fuchsia", not(loom)))]
43        let m = fuchsia_sync::Mutex::new(t);
44        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
45        let m = sync::Mutex::new(t);
46        Mutex(m)
47    }
48
49    /// Acquires a mutex, blocking the current thread until it is able to do so.
50    ///
51    /// # Panics
52    ///
53    /// This method may panic if the calling thread is already holding the
54    /// lock.
55    #[inline]
56    pub fn lock(&self) -> LockGuard<'_, T> {
57        let Self(m) = self;
58        #[cfg(all(target_os = "fuchsia", not(loom)))]
59        return m.lock();
60        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
61        return m.lock().expect("unexpectedly poisoned");
62    }
63
64    /// Consumes this mutex, returning the underlying data.
65    #[inline]
66    pub fn into_inner(self) -> T {
67        let Self(mutex) = self;
68        #[cfg(all(target_os = "fuchsia", not(loom)))]
69        return mutex.into_inner();
70        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
71        return mutex.into_inner().expect("unexpectedly poisoned");
72    }
73
74    /// Returns a mutable reference to the underlying data.
75    ///
76    /// Since this call borrows the [`Mutex`] mutably, no actual locking needs
77    /// to take place.
78    #[inline]
79    // TODO(https://github.com/tokio-rs/loom/pull/322): remove the disable for
80    // loom once loom's lock type supports the method.
81    #[cfg(not(loom))]
82    pub fn get_mut(&mut self) -> &mut T {
83        let Self(mutex) = self;
84        #[cfg(target_os = "fuchsia")]
85        return mutex.get_mut();
86        #[cfg(not(target_os = "fuchsia"))]
87        return mutex.get_mut().expect("unexpectedly poisoned");
88    }
89}
90
91impl<T: 'static> lock_order::lock::ExclusiveLock<T> for Mutex<T> {
92    type Guard<'l> = LockGuard<'l, T>;
93
94    fn lock(&self) -> Self::Guard<'_> {
95        self.lock()
96    }
97}
98
99impl<T, I: Ip> GenericOverIp<I> for Mutex<T>
100where
101    T: GenericOverIp<I>,
102{
103    type Type = Mutex<T::Type>;
104}
105
106/// A reader-writer lock assuming lock poisoning will never occur.
107#[derive(Debug, Default)]
108pub struct RwLock<T>(
109    #[cfg(all(target_os = "fuchsia", not(loom)))] fuchsia_sync::RwLock<T>,
110    #[cfg(not(all(target_os = "fuchsia", not(loom))))] sync::RwLock<T>,
111);
112
113/// Lock guard for read access to a [`RwLock`].
114#[cfg(all(target_os = "fuchsia", not(loom)))]
115pub type RwLockReadGuard<'a, T> = fuchsia_sync::RwLockReadGuard<'a, T>;
116
117/// Lock guard for read access to a [`RwLock`].
118#[cfg(not(all(target_os = "fuchsia", not(loom))))]
119pub type RwLockReadGuard<'a, T> = sync::RwLockReadGuard<'a, T>;
120
121/// Lock guard for write access to a [`RwLock`].
122#[cfg(all(target_os = "fuchsia", not(loom)))]
123pub type RwLockWriteGuard<'a, T> = fuchsia_sync::RwLockWriteGuard<'a, T>;
124
125/// Lock guard for write access to a [`RwLock`].
126#[cfg(not(all(target_os = "fuchsia", not(loom))))]
127pub type RwLockWriteGuard<'a, T> = sync::RwLockWriteGuard<'a, T>;
128
129impl<T> RwLock<T> {
130    /// Creates a new instance of an `RwLock<T>` which is unlocked.
131    #[inline]
132    pub fn new(t: T) -> RwLock<T> {
133        #[cfg(all(target_os = "fuchsia", not(loom)))]
134        let rw = fuchsia_sync::RwLock::new(t);
135        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
136        let rw = sync::RwLock::new(t);
137        RwLock(rw)
138    }
139
140    /// Locks this rwlock with shared read access, blocking the current thread
141    /// until it can be acquired.
142    ///
143    /// # Panics
144    ///
145    /// This method may panic if the calling thread already holds the read or
146    /// write lock.
147    #[inline]
148    pub fn read(&self) -> RwLockReadGuard<'_, T> {
149        let Self(rw) = self;
150        #[cfg(all(target_os = "fuchsia", not(loom)))]
151        return rw.read();
152        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
153        return rw.read().expect("unexpectedly poisoned");
154    }
155
156    /// Locks this rwlock with exclusive write access, blocking the current
157    /// thread until it can be acquired.
158    ///
159    /// # Panics
160    ///
161    /// This method may panic if the calling thread already holds the read or
162    /// write lock.
163    #[inline]
164    pub fn write(&self) -> RwLockWriteGuard<'_, T> {
165        let Self(rw) = self;
166        #[cfg(all(target_os = "fuchsia", not(loom)))]
167        return rw.write();
168        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
169        return rw.write().expect("unexpectedly poisoned");
170    }
171
172    /// Consumes this rwlock, returning the underlying data.
173    #[inline]
174    pub fn into_inner(self) -> T {
175        let Self(rwlock) = self;
176        #[cfg(all(target_os = "fuchsia", not(loom)))]
177        return rwlock.into_inner();
178        #[cfg(not(all(target_os = "fuchsia", not(loom))))]
179        return rwlock.into_inner().expect("unexpectedly poisoned");
180    }
181
182    /// Returns a mutable reference to the underlying data.
183    ///
184    /// Since this call borrows the [`RwLock`] mutably, no actual locking needs
185    /// to take place.
186    #[inline]
187    // TODO(https://github.com/tokio-rs/loom/pull/322): remove the disable for
188    // loom once loom's lock type supports the method.
189    #[cfg(not(loom))]
190    pub fn get_mut(&mut self) -> &mut T {
191        let Self(rwlock) = self;
192        #[cfg(target_os = "fuchsia")]
193        return rwlock.get_mut();
194        #[cfg(not(target_os = "fuchsia"))]
195        return rwlock.get_mut().expect("unexpectedly poisoned");
196    }
197}
198
199impl<T: 'static> lock_order::lock::ReadWriteLock<T> for RwLock<T> {
200    type ReadGuard<'l> = RwLockReadGuard<'l, T>;
201
202    type WriteGuard<'l> = RwLockWriteGuard<'l, T>;
203
204    fn read_lock(&self) -> Self::ReadGuard<'_> {
205        self.read()
206    }
207
208    fn write_lock(&self) -> Self::WriteGuard<'_> {
209        self.write()
210    }
211}
212
213impl<T, I: Ip> GenericOverIp<I> for RwLock<T>
214where
215    T: GenericOverIp<I>,
216{
217    type Type = RwLock<T::Type>;
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223
224    use std::thread;
225
226    #[test]
227    fn mutex_lock_and_write() {
228        let m = Mutex::<u32>::new(0);
229        {
230            let mut guard = m.lock();
231            assert_eq!(*guard, 0);
232            *guard = 5;
233        }
234
235        {
236            let guard = m.lock();
237            assert_eq!(*guard, 5);
238        }
239    }
240
241    #[test]
242    fn mutex_lock_from_different_threads() {
243        const NUM_THREADS: u32 = 4;
244
245        let m = Mutex::<u32>::new(u32::MAX);
246        let m = &m;
247
248        thread::scope(|s| {
249            for i in 0..NUM_THREADS {
250                let _: thread::ScopedJoinHandle<'_, _> = s.spawn(move || {
251                    let prev = {
252                        let mut guard = m.lock();
253                        let prev = *guard;
254                        *guard = i;
255                        prev
256                    };
257
258                    assert!(prev == u32::MAX || prev < NUM_THREADS);
259                });
260            }
261        });
262
263        let guard = m.lock();
264        assert!(*guard < NUM_THREADS);
265    }
266
267    #[test]
268    fn rwlock_read_lock() {
269        let rw = RwLock::<u32>::new(0);
270
271        {
272            let guard = rw.read();
273            assert_eq!(*guard, 0);
274        }
275
276        {
277            let guard = rw.read();
278            assert_eq!(*guard, 0);
279        }
280    }
281
282    #[test]
283    fn rwlock_write_lock() {
284        let rw = RwLock::<u32>::new(0);
285        {
286            let mut guard = rw.write();
287            assert_eq!(*guard, 0);
288            *guard = 5;
289        }
290
291        {
292            let guard = rw.write();
293            assert_eq!(*guard, 5);
294        }
295    }
296
297    #[test]
298    fn rwlock_read_and_write_from_different_threads() {
299        const NUM_THREADS: u32 = 4;
300
301        let rw = RwLock::<u32>::new(u32::MAX);
302        let rw = &rw;
303
304        thread::scope(|s| {
305            for i in 0..NUM_THREADS {
306                let _: thread::ScopedJoinHandle<'_, _> = s.spawn(move || {
307                    let prev = if i % 2 == 0 {
308                        // Only threads with even numbered `i` performs a write.
309                        let mut guard = rw.write();
310                        let prev = *guard;
311                        *guard = i;
312                        prev
313                    } else {
314                        let guard = rw.read();
315                        *guard
316                    };
317
318                    assert!(prev == u32::MAX || (prev < NUM_THREADS && prev % 2 == 0));
319                });
320            }
321        });
322
323        let val = *rw.read();
324        assert!(val < NUM_THREADS && val % 2 == 0);
325    }
326}