Coverage Report

Created: 2026-05-30 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.3/src/sync/rwlock.rs
Line
Count
Source
1
use crate::sync::batch_semaphore::{Semaphore, TryAcquireError};
2
use crate::sync::mutex::TryLockError;
3
#[cfg(all(tokio_unstable, feature = "tracing"))]
4
use crate::util::trace;
5
use std::cell::UnsafeCell;
6
use std::marker;
7
use std::marker::PhantomData;
8
use std::sync::Arc;
9
10
pub(crate) mod owned_read_guard;
11
pub(crate) mod owned_write_guard;
12
pub(crate) mod owned_write_guard_mapped;
13
pub(crate) mod read_guard;
14
pub(crate) mod write_guard;
15
pub(crate) mod write_guard_mapped;
16
pub(crate) use owned_read_guard::OwnedRwLockReadGuard;
17
pub(crate) use owned_write_guard::OwnedRwLockWriteGuard;
18
pub(crate) use owned_write_guard_mapped::OwnedRwLockMappedWriteGuard;
19
pub(crate) use read_guard::RwLockReadGuard;
20
pub(crate) use write_guard::RwLockWriteGuard;
21
pub(crate) use write_guard_mapped::RwLockMappedWriteGuard;
22
23
#[cfg(not(loom))]
24
const MAX_READS: u32 = u32::MAX >> 3;
25
26
#[cfg(loom)]
27
const MAX_READS: u32 = 10;
28
29
/// An asynchronous reader-writer lock.
30
///
31
/// This type of lock allows a number of readers or at most one writer at any
32
/// point in time. The write portion of this lock typically allows modification
33
/// of the underlying data (exclusive access) and the read portion of this lock
34
/// typically allows for read-only access (shared access).
35
///
36
/// In comparison, a [`Mutex`] does not distinguish between readers or writers
37
/// that acquire the lock, therefore causing any tasks waiting for the lock to
38
/// become available to yield. An `RwLock` will allow any number of readers to
39
/// acquire the lock as long as a writer is not holding the lock.
40
///
41
/// The priority policy of Tokio's read-write lock is _fair_ (or
42
/// [_write-preferring_]), in order to ensure that readers cannot starve
43
/// writers. Fairness is ensured using a first-in, first-out queue for the tasks
44
/// awaiting the lock; a read lock will not be given out until all write lock
45
/// requests that were queued before it have been acquired and released. This is
46
/// in contrast to the Rust standard library's `std::sync::RwLock`, where the
47
/// priority policy is dependent on the operating system's implementation.
48
///
49
/// The type parameter `T` represents the data that this lock protects. It is
50
/// required that `T` satisfies [`Send`] to be shared across threads. The RAII guards
51
/// returned from the locking methods implement [`Deref`](trait@std::ops::Deref)
52
/// (and [`DerefMut`](trait@std::ops::DerefMut)
53
/// for the `write` methods) to allow access to the content of the lock.
54
///
55
/// # Examples
56
///
57
/// ```
58
/// use tokio::sync::RwLock;
59
///
60
/// # #[tokio::main(flavor = "current_thread")]
61
/// # async fn main() {
62
/// let lock = RwLock::new(5);
63
///
64
/// // many reader locks can be held at once
65
/// {
66
///     let r1 = lock.read().await;
67
///     let r2 = lock.read().await;
68
///     assert_eq!(*r1, 5);
69
///     assert_eq!(*r2, 5);
70
/// } // read locks are dropped at this point
71
///
72
/// // only one write lock may be held, however
73
/// {
74
///     let mut w = lock.write().await;
75
///     *w += 1;
76
///     assert_eq!(*w, 6);
77
/// } // write lock is dropped here
78
/// # }
79
/// ```
80
///
81
/// [`Mutex`]: struct@super::Mutex
82
/// [`RwLock`]: struct@RwLock
83
/// [`RwLockReadGuard`]: struct@RwLockReadGuard
84
/// [`RwLockWriteGuard`]: struct@RwLockWriteGuard
85
/// [`Send`]: trait@std::marker::Send
86
/// [_write-preferring_]: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock#Priority_policies
87
pub struct RwLock<T: ?Sized> {
88
    #[cfg(all(tokio_unstable, feature = "tracing"))]
89
    resource_span: tracing::Span,
90
91
    // maximum number of concurrent readers
92
    mr: u32,
93
94
    //semaphore to coordinate read and write access to T
95
    s: Semaphore,
96
97
    //inner data T
98
    c: UnsafeCell<T>,
99
}
100
101
#[test]
102
#[cfg(not(loom))]
103
fn bounds() {
104
    fn check_send<T: Send>() {}
105
    fn check_sync<T: Sync>() {}
106
    fn check_unpin<T: Unpin>() {}
107
    // This has to take a value, since the async fn's return type is unnameable.
108
    fn check_send_sync_val<T: Send + Sync>(_t: T) {}
109
110
    check_send::<RwLock<u32>>();
111
    check_sync::<RwLock<u32>>();
112
    check_unpin::<RwLock<u32>>();
113
114
    check_send::<RwLockReadGuard<'_, u32>>();
115
    check_sync::<RwLockReadGuard<'_, u32>>();
116
    check_unpin::<RwLockReadGuard<'_, u32>>();
117
118
    check_send::<OwnedRwLockReadGuard<u32, i32>>();
119
    check_sync::<OwnedRwLockReadGuard<u32, i32>>();
120
    check_unpin::<OwnedRwLockReadGuard<u32, i32>>();
121
122
    check_send::<RwLockWriteGuard<'_, u32>>();
123
    check_sync::<RwLockWriteGuard<'_, u32>>();
124
    check_unpin::<RwLockWriteGuard<'_, u32>>();
125
126
    check_send::<RwLockMappedWriteGuard<'_, u32>>();
127
    check_sync::<RwLockMappedWriteGuard<'_, u32>>();
128
    check_unpin::<RwLockMappedWriteGuard<'_, u32>>();
129
130
    check_send::<OwnedRwLockWriteGuard<u32>>();
131
    check_sync::<OwnedRwLockWriteGuard<u32>>();
132
    check_unpin::<OwnedRwLockWriteGuard<u32>>();
133
134
    check_send::<OwnedRwLockMappedWriteGuard<u32, i32>>();
135
    check_sync::<OwnedRwLockMappedWriteGuard<u32, i32>>();
136
    check_unpin::<OwnedRwLockMappedWriteGuard<u32, i32>>();
137
138
    let rwlock = Arc::new(RwLock::new(0));
139
    check_send_sync_val(rwlock.read());
140
    check_send_sync_val(Arc::clone(&rwlock).read_owned());
141
    check_send_sync_val(rwlock.write());
142
    check_send_sync_val(Arc::clone(&rwlock).write_owned());
143
}
144
145
// As long as T: Send + Sync, it's fine to send and share RwLock<T> between threads.
146
// If T were not Send, sending and sharing a RwLock<T> would be bad, since you can access T through
147
// RwLock<T>.
148
unsafe impl<T> Send for RwLock<T> where T: ?Sized + Send {}
149
unsafe impl<T> Sync for RwLock<T> where T: ?Sized + Send + Sync {}
150
// NB: These impls need to be explicit since we're storing a raw pointer.
151
// Safety: Stores a raw pointer to `T`, so if `T` is `Sync`, the lock guard over
152
// `T` is `Send`.
153
unsafe impl<T> Send for RwLockReadGuard<'_, T> where T: ?Sized + Sync {}
154
unsafe impl<T> Sync for RwLockReadGuard<'_, T> where T: ?Sized + Send + Sync {}
155
// T is required to be `Send` because an OwnedRwLockReadGuard can be used to drop the value held in
156
// the RwLock, unlike RwLockReadGuard.
157
unsafe impl<T, U> Send for OwnedRwLockReadGuard<T, U>
158
where
159
    T: ?Sized + Send + Sync,
160
    U: ?Sized + Sync,
161
{
162
}
163
unsafe impl<T, U> Sync for OwnedRwLockReadGuard<T, U>
164
where
165
    T: ?Sized + Send + Sync,
166
    U: ?Sized + Send + Sync,
167
{
168
}
169
unsafe impl<T> Sync for RwLockWriteGuard<'_, T> where T: ?Sized + Send + Sync {}
170
unsafe impl<T> Sync for OwnedRwLockWriteGuard<T> where T: ?Sized + Send + Sync {}
171
unsafe impl<T> Sync for RwLockMappedWriteGuard<'_, T> where T: ?Sized + Send + Sync {}
172
unsafe impl<T, U> Sync for OwnedRwLockMappedWriteGuard<T, U>
173
where
174
    T: ?Sized + Send + Sync,
175
    U: ?Sized + Send + Sync,
176
{
177
}
178
// Safety: Stores a raw pointer to `T`, so if `T` is `Sync`, the lock guard over
179
// `T` is `Send` - but since this is also provides mutable access, we need to
180
// make sure that `T` is `Send` since its value can be sent across thread
181
// boundaries.
182
unsafe impl<T> Send for RwLockWriteGuard<'_, T> where T: ?Sized + Send + Sync {}
183
unsafe impl<T> Send for OwnedRwLockWriteGuard<T> where T: ?Sized + Send + Sync {}
184
unsafe impl<T> Send for RwLockMappedWriteGuard<'_, T> where T: ?Sized + Send + Sync {}
185
unsafe impl<T, U> Send for OwnedRwLockMappedWriteGuard<T, U>
186
where
187
    T: ?Sized + Send + Sync,
188
    U: ?Sized + Send + Sync,
189
{
190
}
191
192
impl<T: ?Sized> RwLock<T> {
193
    /// Creates a new instance of an `RwLock<T>` which is unlocked.
194
    ///
195
    /// # Examples
196
    ///
197
    /// ```
198
    /// use tokio::sync::RwLock;
199
    ///
200
    /// let lock = RwLock::new(5);
201
    /// ```
202
    #[track_caller]
203
0
    pub fn new(value: T) -> RwLock<T>
204
0
    where
205
0
        T: Sized,
206
    {
207
        #[cfg(all(tokio_unstable, feature = "tracing"))]
208
        let resource_span = {
209
            let location = std::panic::Location::caller();
210
            let resource_span = tracing::trace_span!(
211
                parent: None,
212
                "runtime.resource",
213
                concrete_type = "RwLock",
214
                kind = "Sync",
215
                loc.file = location.file(),
216
                loc.line = location.line(),
217
                loc.col = location.column(),
218
            );
219
220
            resource_span.in_scope(|| {
221
                tracing::trace!(
222
                    target: "runtime::resource::state_update",
223
                    max_readers = MAX_READS,
224
                );
225
226
                tracing::trace!(
227
                    target: "runtime::resource::state_update",
228
                    write_locked = false,
229
                );
230
231
                tracing::trace!(
232
                    target: "runtime::resource::state_update",
233
                    current_readers = 0,
234
                );
235
            });
236
237
            resource_span
238
        };
239
240
        #[cfg(all(tokio_unstable, feature = "tracing"))]
241
        let s = resource_span.in_scope(|| Semaphore::new(MAX_READS as usize));
242
243
        #[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
244
0
        let s = Semaphore::new(MAX_READS as usize);
245
246
0
        RwLock {
247
0
            mr: MAX_READS,
248
0
            c: UnsafeCell::new(value),
249
0
            s,
250
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
251
0
            resource_span,
252
0
        }
253
0
    }
254
255
    /// Creates a new instance of an `RwLock<T>` which is unlocked
256
    /// and allows a maximum of `max_reads` concurrent readers.
257
    ///
258
    /// # Examples
259
    ///
260
    /// ```
261
    /// use tokio::sync::RwLock;
262
    ///
263
    /// let lock = RwLock::with_max_readers(5, 1024);
264
    /// ```
265
    ///
266
    /// # Panics
267
    ///
268
    /// Panics if `max_reads` is `0` or is bigger than `u32::MAX >> 3`.
269
    #[track_caller]
270
0
    pub fn with_max_readers(value: T, max_reads: u32) -> RwLock<T>
271
0
    where
272
0
        T: Sized,
273
    {
274
0
        assert_ne!(max_reads, 0, "a RwLock may not be created with 0 readers");
275
0
        assert!(
276
0
            max_reads <= MAX_READS,
277
0
            "a RwLock may not be created with more than {MAX_READS} readers"
278
        );
279
280
        #[cfg(all(tokio_unstable, feature = "tracing"))]
281
        let resource_span = {
282
            let location = std::panic::Location::caller();
283
284
            let resource_span = tracing::trace_span!(
285
                parent: None,
286
                "runtime.resource",
287
                concrete_type = "RwLock",
288
                kind = "Sync",
289
                loc.file = location.file(),
290
                loc.line = location.line(),
291
                loc.col = location.column(),
292
            );
293
294
            resource_span.in_scope(|| {
295
                tracing::trace!(
296
                    target: "runtime::resource::state_update",
297
                    max_readers = max_reads,
298
                );
299
300
                tracing::trace!(
301
                    target: "runtime::resource::state_update",
302
                    write_locked = false,
303
                );
304
305
                tracing::trace!(
306
                    target: "runtime::resource::state_update",
307
                    current_readers = 0,
308
                );
309
            });
310
311
            resource_span
312
        };
313
314
        #[cfg(all(tokio_unstable, feature = "tracing"))]
315
        let s = resource_span.in_scope(|| Semaphore::new(max_reads as usize));
316
317
        #[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
318
0
        let s = Semaphore::new(max_reads as usize);
319
320
0
        RwLock {
321
0
            mr: max_reads,
322
0
            c: UnsafeCell::new(value),
323
0
            s,
324
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
325
0
            resource_span,
326
0
        }
327
0
    }
328
329
    /// Creates a new instance of an `RwLock<T>` which is unlocked.
330
    ///
331
    /// When using the `tracing` [unstable feature], a `RwLock` created with
332
    /// `const_new` will not be instrumented. As such, it will not be visible
333
    /// in [`tokio-console`]. Instead, [`RwLock::new`] should be used to create
334
    /// an instrumented object if that is needed.
335
    ///
336
    /// # Examples
337
    ///
338
    /// ```
339
    /// use tokio::sync::RwLock;
340
    ///
341
    /// static LOCK: RwLock<i32> = RwLock::const_new(5);
342
    /// ```
343
    ///
344
    /// [`tokio-console`]: https://github.com/tokio-rs/console
345
    /// [unstable feature]: crate#unstable-features
346
    #[cfg(not(all(loom, test)))]
347
0
    pub const fn const_new(value: T) -> RwLock<T>
348
0
    where
349
0
        T: Sized,
350
    {
351
0
        RwLock {
352
0
            mr: MAX_READS,
353
0
            c: UnsafeCell::new(value),
354
0
            s: Semaphore::const_new(MAX_READS as usize),
355
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
356
0
            resource_span: tracing::Span::none(),
357
0
        }
358
0
    }
359
360
    /// Creates a new instance of an `RwLock<T>` which is unlocked
361
    /// and allows a maximum of `max_reads` concurrent readers.
362
    ///
363
    /// # Examples
364
    ///
365
    /// ```
366
    /// use tokio::sync::RwLock;
367
    ///
368
    /// static LOCK: RwLock<i32> = RwLock::const_with_max_readers(5, 1024);
369
    /// ```
370
    ///
371
    /// # Panics
372
    ///
373
    /// Panics if `max_reads` is `0` or is bigger than `u32::MAX >> 3`.
374
    #[cfg(not(all(loom, test)))]
375
0
    pub const fn const_with_max_readers(value: T, max_reads: u32) -> RwLock<T>
376
0
    where
377
0
        T: Sized,
378
    {
379
0
        assert!(max_reads != 0, "a RwLock may not be created with 0 readers");
380
0
        assert!(max_reads <= MAX_READS);
381
382
0
        RwLock {
383
0
            mr: max_reads,
384
0
            c: UnsafeCell::new(value),
385
0
            s: Semaphore::const_new(max_reads as usize),
386
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
387
0
            resource_span: tracing::Span::none(),
388
0
        }
389
0
    }
390
391
    /// Locks this `RwLock` with shared read access, causing the current task
392
    /// to yield until the lock has been acquired.
393
    ///
394
    /// The calling task will yield until there are no writers which hold the
395
    /// lock. There may be other readers inside the lock when the task resumes.
396
    ///
397
    /// Note that under the priority policy of [`RwLock`], read locks are not
398
    /// granted until prior write locks, to prevent starvation. Therefore
399
    /// deadlock may occur if a read lock is held by the current task, a write
400
    /// lock attempt is made, and then a subsequent read lock attempt is made
401
    /// by the current task.
402
    ///
403
    /// Returns an RAII guard which will drop this read access of the `RwLock`
404
    /// when dropped.
405
    ///
406
    /// # Cancel safety
407
    ///
408
    /// This method uses a queue to fairly distribute locks in the order they
409
    /// were requested. Cancelling a call to `read` makes you lose your place in
410
    /// the queue.
411
    ///
412
    /// # Examples
413
    ///
414
    /// ```
415
    /// use std::sync::Arc;
416
    /// use tokio::sync::RwLock;
417
    ///
418
    /// # #[tokio::main(flavor = "current_thread")]
419
    /// # async fn main() {
420
    /// let lock = Arc::new(RwLock::new(1));
421
    /// let c_lock = lock.clone();
422
    ///
423
    /// let n = lock.read().await;
424
    /// assert_eq!(*n, 1);
425
    ///
426
    /// tokio::spawn(async move {
427
    ///     // While main has an active read lock, we acquire one too.
428
    ///     let r = c_lock.read().await;
429
    ///     assert_eq!(*r, 1);
430
    /// }).await.expect("The spawned task has panicked");
431
    ///
432
    /// // Drop the guard after the spawned task finishes.
433
    /// drop(n);
434
    /// # }
435
    /// ```
436
0
    pub async fn read(&self) -> RwLockReadGuard<'_, T> {
437
0
        let acquire_fut = async {
438
0
            self.s.acquire(1).await.unwrap_or_else(|_| {
439
                // The semaphore was closed. but, we never explicitly close it, and we have a
440
                // handle to it through the Arc, which means that this can never happen.
441
0
                unreachable!()
442
            });
443
444
0
            RwLockReadGuard {
445
0
                s: &self.s,
446
0
                data: self.c.get(),
447
0
                marker: PhantomData,
448
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
449
0
                resource_span: self.resource_span.clone(),
450
0
            }
451
0
        };
452
453
        #[cfg(all(tokio_unstable, feature = "tracing"))]
454
        let acquire_fut = trace::async_op(
455
            move || acquire_fut,
456
            self.resource_span.clone(),
457
            "RwLock::read",
458
            "poll",
459
            false,
460
        );
461
462
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
463
0
        let guard = acquire_fut.await;
464
465
        #[cfg(all(tokio_unstable, feature = "tracing"))]
466
        self.resource_span.in_scope(|| {
467
            tracing::trace!(
468
            target: "runtime::resource::state_update",
469
            current_readers = 1,
470
            current_readers.op = "add",
471
            )
472
        });
473
474
0
        guard
475
0
    }
476
477
    /// Blockingly locks this `RwLock` with shared read access.
478
    ///
479
    /// This method is intended for use cases where you
480
    /// need to use this rwlock in asynchronous code as well as in synchronous code.
481
    ///
482
    /// Returns an RAII guard which will drop the read access of this `RwLock` when dropped.
483
    ///
484
    /// # Panics
485
    ///
486
    /// This function panics if called within an asynchronous execution context.
487
    ///
488
    ///   - If you find yourself in an asynchronous execution context and needing
489
    ///     to call some (synchronous) function which performs one of these
490
    ///     `blocking_` operations, then consider wrapping that call inside
491
    ///     [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
492
    ///     (or [`block_in_place()`][crate::task::block_in_place]).
493
    ///
494
    /// # Examples
495
    ///
496
    /// ```
497
    /// # #[cfg(not(target_family = "wasm"))]
498
    /// # {
499
    /// use std::sync::Arc;
500
    /// use tokio::sync::RwLock;
501
    ///
502
    /// #[tokio::main]
503
    /// async fn main() {
504
    ///     let rwlock = Arc::new(RwLock::new(1));
505
    ///     let mut write_lock = rwlock.write().await;
506
    ///
507
    ///     let blocking_task = tokio::task::spawn_blocking({
508
    ///         let rwlock = Arc::clone(&rwlock);
509
    ///         move || {
510
    ///             // This shall block until the `write_lock` is released.
511
    ///             let read_lock = rwlock.blocking_read();
512
    ///             assert_eq!(*read_lock, 0);
513
    ///         }
514
    ///     });
515
    ///
516
    ///     *write_lock -= 1;
517
    ///     drop(write_lock); // release the lock.
518
    ///
519
    ///     // Await the completion of the blocking task.
520
    ///     blocking_task.await.unwrap();
521
    ///
522
    ///     // Assert uncontended.
523
    ///     assert!(rwlock.try_write().is_ok());
524
    /// }
525
    /// # }
526
    /// ```
527
    #[track_caller]
528
    #[cfg(feature = "sync")]
529
0
    pub fn blocking_read(&self) -> RwLockReadGuard<'_, T> {
530
0
        crate::future::block_on(self.read())
531
0
    }
532
533
    /// Locks this `RwLock` with shared read access, causing the current task
534
    /// to yield until the lock has been acquired.
535
    ///
536
    /// The calling task will yield until there are no writers which hold the
537
    /// lock. There may be other readers inside the lock when the task resumes.
538
    ///
539
    /// This method is identical to [`RwLock::read`], except that the returned
540
    /// guard references the `RwLock` with an [`Arc`] rather than by borrowing
541
    /// it. Therefore, the `RwLock` must be wrapped in an `Arc` to call this
542
    /// method, and the guard will live for the `'static` lifetime, as it keeps
543
    /// the `RwLock` alive by holding an `Arc`.
544
    ///
545
    /// Note that under the priority policy of [`RwLock`], read locks are not
546
    /// granted until prior write locks, to prevent starvation. Therefore
547
    /// deadlock may occur if a read lock is held by the current task, a write
548
    /// lock attempt is made, and then a subsequent read lock attempt is made
549
    /// by the current task.
550
    ///
551
    /// Returns an RAII guard which will drop this read access of the `RwLock`
552
    /// when dropped.
553
    ///
554
    /// # Cancel safety
555
    ///
556
    /// This method uses a queue to fairly distribute locks in the order they
557
    /// were requested. Cancelling a call to `read_owned` makes you lose your
558
    /// place in the queue.
559
    ///
560
    /// # Examples
561
    ///
562
    /// ```
563
    /// use std::sync::Arc;
564
    /// use tokio::sync::RwLock;
565
    ///
566
    /// # #[tokio::main(flavor = "current_thread")]
567
    /// # async fn main() {
568
    /// let lock = Arc::new(RwLock::new(1));
569
    /// let c_lock = lock.clone();
570
    ///
571
    /// let n = lock.read_owned().await;
572
    /// assert_eq!(*n, 1);
573
    ///
574
    /// tokio::spawn(async move {
575
    ///     // While main has an active read lock, we acquire one too.
576
    ///     let r = c_lock.read_owned().await;
577
    ///     assert_eq!(*r, 1);
578
    /// }).await.expect("The spawned task has panicked");
579
    ///
580
    /// // Drop the guard after the spawned task finishes.
581
    /// drop(n);
582
    ///}
583
    /// ```
584
0
    pub async fn read_owned(self: Arc<Self>) -> OwnedRwLockReadGuard<T> {
585
        #[cfg(all(tokio_unstable, feature = "tracing"))]
586
        let resource_span = self.resource_span.clone();
587
588
0
        let acquire_fut = async {
589
0
            self.s.acquire(1).await.unwrap_or_else(|_| {
590
                // The semaphore was closed. but, we never explicitly close it, and we have a
591
                // handle to it through the Arc, which means that this can never happen.
592
0
                unreachable!()
593
            });
594
595
0
            OwnedRwLockReadGuard {
596
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
597
0
                resource_span: self.resource_span.clone(),
598
0
                data: self.c.get(),
599
0
                lock: self,
600
0
                _p: PhantomData,
601
0
            }
602
0
        };
603
604
        #[cfg(all(tokio_unstable, feature = "tracing"))]
605
        let acquire_fut = trace::async_op(
606
            move || acquire_fut,
607
            resource_span,
608
            "RwLock::read_owned",
609
            "poll",
610
            false,
611
        );
612
613
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
614
0
        let guard = acquire_fut.await;
615
616
        #[cfg(all(tokio_unstable, feature = "tracing"))]
617
        guard.resource_span.in_scope(|| {
618
            tracing::trace!(
619
            target: "runtime::resource::state_update",
620
            current_readers = 1,
621
            current_readers.op = "add",
622
            )
623
        });
624
625
0
        guard
626
0
    }
627
628
    /// Attempts to acquire this `RwLock` with shared read access.
629
    ///
630
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
631
    /// Otherwise, an RAII guard is returned which will release read access
632
    /// when dropped.
633
    ///
634
    /// [`TryLockError`]: TryLockError
635
    ///
636
    /// # Examples
637
    ///
638
    /// ```
639
    /// use std::sync::Arc;
640
    /// use tokio::sync::RwLock;
641
    ///
642
    /// # #[tokio::main(flavor = "current_thread")]
643
    /// # async fn main() {
644
    /// let lock = Arc::new(RwLock::new(1));
645
    /// let c_lock = lock.clone();
646
    ///
647
    /// let v = lock.try_read().unwrap();
648
    /// assert_eq!(*v, 1);
649
    ///
650
    /// tokio::spawn(async move {
651
    ///     // While main has an active read lock, we acquire one too.
652
    ///     let n = c_lock.read().await;
653
    ///     assert_eq!(*n, 1);
654
    /// }).await.expect("The spawned task has panicked");
655
    ///
656
    /// // Drop the guard when spawned task finishes.
657
    /// drop(v);
658
    /// # }
659
    /// ```
660
0
    pub fn try_read(&self) -> Result<RwLockReadGuard<'_, T>, TryLockError> {
661
0
        match self.s.try_acquire(1) {
662
0
            Ok(permit) => permit,
663
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
664
0
            Err(TryAcquireError::Closed) => unreachable!(),
665
        }
666
667
0
        let guard = RwLockReadGuard {
668
0
            s: &self.s,
669
0
            data: self.c.get(),
670
0
            marker: marker::PhantomData,
671
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
672
0
            resource_span: self.resource_span.clone(),
673
0
        };
674
675
        #[cfg(all(tokio_unstable, feature = "tracing"))]
676
        self.resource_span.in_scope(|| {
677
            tracing::trace!(
678
            target: "runtime::resource::state_update",
679
            current_readers = 1,
680
            current_readers.op = "add",
681
            )
682
        });
683
684
0
        Ok(guard)
685
0
    }
686
687
    /// Attempts to acquire this `RwLock` with shared read access.
688
    ///
689
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
690
    /// Otherwise, an RAII guard is returned which will release read access
691
    /// when dropped.
692
    ///
693
    /// This method is identical to [`RwLock::try_read`], except that the
694
    /// returned guard references the `RwLock` with an [`Arc`] rather than by
695
    /// borrowing it. Therefore, the `RwLock` must be wrapped in an `Arc` to
696
    /// call this method, and the guard will live for the `'static` lifetime,
697
    /// as it keeps the `RwLock` alive by holding an `Arc`.
698
    ///
699
    /// [`TryLockError`]: TryLockError
700
    ///
701
    /// # Examples
702
    ///
703
    /// ```
704
    /// use std::sync::Arc;
705
    /// use tokio::sync::RwLock;
706
    ///
707
    /// # #[tokio::main(flavor = "current_thread")]
708
    /// # async fn main() {
709
    /// let lock = Arc::new(RwLock::new(1));
710
    /// let c_lock = lock.clone();
711
    ///
712
    /// let v = lock.try_read_owned().unwrap();
713
    /// assert_eq!(*v, 1);
714
    ///
715
    /// tokio::spawn(async move {
716
    ///     // While main has an active read lock, we acquire one too.
717
    ///     let n = c_lock.read_owned().await;
718
    ///     assert_eq!(*n, 1);
719
    /// }).await.expect("The spawned task has panicked");
720
    ///
721
    /// // Drop the guard when spawned task finishes.
722
    /// drop(v);
723
    /// # }
724
    /// ```
725
0
    pub fn try_read_owned(self: Arc<Self>) -> Result<OwnedRwLockReadGuard<T>, TryLockError> {
726
0
        match self.s.try_acquire(1) {
727
0
            Ok(permit) => permit,
728
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
729
0
            Err(TryAcquireError::Closed) => unreachable!(),
730
        }
731
732
0
        let guard = OwnedRwLockReadGuard {
733
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
734
0
            resource_span: self.resource_span.clone(),
735
0
            data: self.c.get(),
736
0
            lock: self,
737
0
            _p: PhantomData,
738
0
        };
739
740
        #[cfg(all(tokio_unstable, feature = "tracing"))]
741
        guard.resource_span.in_scope(|| {
742
            tracing::trace!(
743
            target: "runtime::resource::state_update",
744
            current_readers = 1,
745
            current_readers.op = "add",
746
            )
747
        });
748
749
0
        Ok(guard)
750
0
    }
751
752
    /// Locks this `RwLock` with exclusive write access, causing the current
753
    /// task to yield until the lock has been acquired.
754
    ///
755
    /// The calling task will yield while other writers or readers currently
756
    /// have access to the lock.
757
    ///
758
    /// Returns an RAII guard which will drop the write access of this `RwLock`
759
    /// when dropped.
760
    ///
761
    /// # Cancel safety
762
    ///
763
    /// This method uses a queue to fairly distribute locks in the order they
764
    /// were requested. Cancelling a call to `write` makes you lose your place
765
    /// in the queue.
766
    ///
767
    /// # Examples
768
    ///
769
    /// ```
770
    /// use tokio::sync::RwLock;
771
    ///
772
    /// # #[tokio::main(flavor = "current_thread")]
773
    /// # async fn main() {
774
    /// let lock = RwLock::new(1);
775
    ///
776
    /// let mut n = lock.write().await;
777
    /// *n = 2;
778
    /// # }
779
    /// ```
780
0
    pub async fn write(&self) -> RwLockWriteGuard<'_, T> {
781
0
        let acquire_fut = async {
782
0
            debug_assert_ne!(self.mr, 0);
783
0
            self.s.acquire(self.mr as usize).await.unwrap_or_else(|_| {
784
                // The semaphore was closed. but, we never explicitly close it, and we have a
785
                // handle to it through the Arc, which means that this can never happen.
786
0
                unreachable!()
787
            });
788
789
0
            RwLockWriteGuard {
790
0
                permits_acquired: self.mr,
791
0
                s: &self.s,
792
0
                data: self.c.get(),
793
0
                marker: marker::PhantomData,
794
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
795
0
                resource_span: self.resource_span.clone(),
796
0
            }
797
0
        };
798
799
        #[cfg(all(tokio_unstable, feature = "tracing"))]
800
        let acquire_fut = trace::async_op(
801
            move || acquire_fut,
802
            self.resource_span.clone(),
803
            "RwLock::write",
804
            "poll",
805
            false,
806
        );
807
808
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
809
0
        let guard = acquire_fut.await;
810
811
        #[cfg(all(tokio_unstable, feature = "tracing"))]
812
        self.resource_span.in_scope(|| {
813
            tracing::trace!(
814
            target: "runtime::resource::state_update",
815
            write_locked = true,
816
            write_locked.op = "override",
817
            )
818
        });
819
820
0
        guard
821
0
    }
822
823
    /// Blockingly locks this `RwLock` with exclusive write access.
824
    ///
825
    /// This method is intended for use cases where you
826
    /// need to use this rwlock in asynchronous code as well as in synchronous code.
827
    ///
828
    /// Returns an RAII guard which will drop the write access of this `RwLock` when dropped.
829
    ///
830
    /// # Panics
831
    ///
832
    /// This function panics if called within an asynchronous execution context.
833
    ///
834
    ///   - If you find yourself in an asynchronous execution context and needing
835
    ///     to call some (synchronous) function which performs one of these
836
    ///     `blocking_` operations, then consider wrapping that call inside
837
    ///     [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
838
    ///     (or [`block_in_place()`][crate::task::block_in_place]).
839
    ///
840
    /// # Examples
841
    ///
842
    /// ```
843
    /// # #[cfg(not(target_family = "wasm"))]
844
    /// # {
845
    /// use std::sync::Arc;
846
    /// use tokio::{sync::RwLock};
847
    ///
848
    /// #[tokio::main]
849
    /// async fn main() {
850
    ///     let rwlock =  Arc::new(RwLock::new(1));
851
    ///     let read_lock = rwlock.read().await;
852
    ///
853
    ///     let blocking_task = tokio::task::spawn_blocking({
854
    ///         let rwlock = Arc::clone(&rwlock);
855
    ///         move || {
856
    ///             // This shall block until the `read_lock` is released.
857
    ///             let mut write_lock = rwlock.blocking_write();
858
    ///             *write_lock = 2;
859
    ///         }
860
    ///     });
861
    ///
862
    ///     assert_eq!(*read_lock, 1);
863
    ///     // Release the last outstanding read lock.
864
    ///     drop(read_lock);
865
    ///
866
    ///     // Await the completion of the blocking task.
867
    ///     blocking_task.await.unwrap();
868
    ///
869
    ///     // Assert uncontended.
870
    ///     let read_lock = rwlock.try_read().unwrap();
871
    ///     assert_eq!(*read_lock, 2);
872
    /// }
873
    /// # }
874
    /// ```
875
    #[track_caller]
876
    #[cfg(feature = "sync")]
877
0
    pub fn blocking_write(&self) -> RwLockWriteGuard<'_, T> {
878
0
        crate::future::block_on(self.write())
879
0
    }
880
881
    /// Locks this `RwLock` with exclusive write access, causing the current
882
    /// task to yield until the lock has been acquired.
883
    ///
884
    /// The calling task will yield while other writers or readers currently
885
    /// have access to the lock.
886
    ///
887
    /// This method is identical to [`RwLock::write`], except that the returned
888
    /// guard references the `RwLock` with an [`Arc`] rather than by borrowing
889
    /// it. Therefore, the `RwLock` must be wrapped in an `Arc` to call this
890
    /// method, and the guard will live for the `'static` lifetime, as it keeps
891
    /// the `RwLock` alive by holding an `Arc`.
892
    ///
893
    /// Returns an RAII guard which will drop the write access of this `RwLock`
894
    /// when dropped.
895
    ///
896
    /// # Cancel safety
897
    ///
898
    /// This method uses a queue to fairly distribute locks in the order they
899
    /// were requested. Cancelling a call to `write_owned` makes you lose your
900
    /// place in the queue.
901
    ///
902
    /// # Examples
903
    ///
904
    /// ```
905
    /// use std::sync::Arc;
906
    /// use tokio::sync::RwLock;
907
    ///
908
    /// # #[tokio::main(flavor = "current_thread")]
909
    /// # async fn main() {
910
    /// let lock = Arc::new(RwLock::new(1));
911
    ///
912
    /// let mut n = lock.write_owned().await;
913
    /// *n = 2;
914
    ///}
915
    /// ```
916
0
    pub async fn write_owned(self: Arc<Self>) -> OwnedRwLockWriteGuard<T> {
917
        #[cfg(all(tokio_unstable, feature = "tracing"))]
918
        let resource_span = self.resource_span.clone();
919
920
0
        let acquire_fut = async {
921
0
            debug_assert_ne!(self.mr, 0);
922
0
            self.s.acquire(self.mr as usize).await.unwrap_or_else(|_| {
923
                // The semaphore was closed. but, we never explicitly close it, and we have a
924
                // handle to it through the Arc, which means that this can never happen.
925
0
                unreachable!()
926
            });
927
928
0
            OwnedRwLockWriteGuard {
929
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
930
0
                resource_span: self.resource_span.clone(),
931
0
                permits_acquired: self.mr,
932
0
                data: self.c.get(),
933
0
                lock: self,
934
0
                _p: PhantomData,
935
0
            }
936
0
        };
937
938
        #[cfg(all(tokio_unstable, feature = "tracing"))]
939
        let acquire_fut = trace::async_op(
940
            move || acquire_fut,
941
            resource_span,
942
            "RwLock::write_owned",
943
            "poll",
944
            false,
945
        );
946
947
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
948
0
        let guard = acquire_fut.await;
949
950
        #[cfg(all(tokio_unstable, feature = "tracing"))]
951
        guard.resource_span.in_scope(|| {
952
            tracing::trace!(
953
            target: "runtime::resource::state_update",
954
            write_locked = true,
955
            write_locked.op = "override",
956
            )
957
        });
958
959
0
        guard
960
0
    }
961
962
    /// Attempts to acquire this `RwLock` with exclusive write access.
963
    ///
964
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
965
    /// Otherwise, an RAII guard is returned which will release write access
966
    /// when dropped.
967
    ///
968
    /// [`TryLockError`]: TryLockError
969
    ///
970
    /// # Examples
971
    ///
972
    /// ```
973
    /// use tokio::sync::RwLock;
974
    ///
975
    /// # #[tokio::main(flavor = "current_thread")]
976
    /// # async fn main() {
977
    /// let rw = RwLock::new(1);
978
    ///
979
    /// let v = rw.read().await;
980
    /// assert_eq!(*v, 1);
981
    ///
982
    /// assert!(rw.try_write().is_err());
983
    /// # }
984
    /// ```
985
0
    pub fn try_write(&self) -> Result<RwLockWriteGuard<'_, T>, TryLockError> {
986
0
        debug_assert_ne!(self.mr, 0);
987
0
        match self.s.try_acquire(self.mr as usize) {
988
0
            Ok(permit) => permit,
989
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
990
0
            Err(TryAcquireError::Closed) => unreachable!(),
991
        }
992
993
0
        let guard = RwLockWriteGuard {
994
0
            permits_acquired: self.mr,
995
0
            s: &self.s,
996
0
            data: self.c.get(),
997
0
            marker: marker::PhantomData,
998
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
999
0
            resource_span: self.resource_span.clone(),
1000
0
        };
1001
1002
        #[cfg(all(tokio_unstable, feature = "tracing"))]
1003
        self.resource_span.in_scope(|| {
1004
            tracing::trace!(
1005
            target: "runtime::resource::state_update",
1006
            write_locked = true,
1007
            write_locked.op = "override",
1008
            )
1009
        });
1010
1011
0
        Ok(guard)
1012
0
    }
1013
1014
    /// Attempts to acquire this `RwLock` with exclusive write access.
1015
    ///
1016
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
1017
    /// Otherwise, an RAII guard is returned which will release write access
1018
    /// when dropped.
1019
    ///
1020
    /// This method is identical to [`RwLock::try_write`], except that the
1021
    /// returned guard references the `RwLock` with an [`Arc`] rather than by
1022
    /// borrowing it. Therefore, the `RwLock` must be wrapped in an `Arc` to
1023
    /// call this method, and the guard will live for the `'static` lifetime,
1024
    /// as it keeps the `RwLock` alive by holding an `Arc`.
1025
    ///
1026
    /// [`TryLockError`]: TryLockError
1027
    ///
1028
    /// # Examples
1029
    ///
1030
    /// ```
1031
    /// use std::sync::Arc;
1032
    /// use tokio::sync::RwLock;
1033
    ///
1034
    /// # #[tokio::main(flavor = "current_thread")]
1035
    /// # async fn main() {
1036
    /// let rw = Arc::new(RwLock::new(1));
1037
    ///
1038
    /// let v = Arc::clone(&rw).read_owned().await;
1039
    /// assert_eq!(*v, 1);
1040
    ///
1041
    /// assert!(rw.try_write_owned().is_err());
1042
    /// # }
1043
    /// ```
1044
0
    pub fn try_write_owned(self: Arc<Self>) -> Result<OwnedRwLockWriteGuard<T>, TryLockError> {
1045
0
        debug_assert_ne!(self.mr, 0);
1046
0
        match self.s.try_acquire(self.mr as usize) {
1047
0
            Ok(permit) => permit,
1048
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
1049
0
            Err(TryAcquireError::Closed) => unreachable!(),
1050
        }
1051
1052
0
        let guard = OwnedRwLockWriteGuard {
1053
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
1054
0
            resource_span: self.resource_span.clone(),
1055
0
            permits_acquired: self.mr,
1056
0
            data: self.c.get(),
1057
0
            lock: self,
1058
0
            _p: PhantomData,
1059
0
        };
1060
1061
        #[cfg(all(tokio_unstable, feature = "tracing"))]
1062
        guard.resource_span.in_scope(|| {
1063
            tracing::trace!(
1064
            target: "runtime::resource::state_update",
1065
            write_locked = true,
1066
            write_locked.op = "override",
1067
            )
1068
        });
1069
1070
0
        Ok(guard)
1071
0
    }
1072
1073
    /// Returns a mutable reference to the underlying data.
1074
    ///
1075
    /// Since this call borrows the `RwLock` mutably, no actual locking needs to
1076
    /// take place -- the mutable borrow statically guarantees no locks exist.
1077
    ///
1078
    /// # Examples
1079
    ///
1080
    /// ```
1081
    /// use tokio::sync::RwLock;
1082
    ///
1083
    /// fn main() {
1084
    ///     let mut lock = RwLock::new(1);
1085
    ///
1086
    ///     let n = lock.get_mut();
1087
    ///     *n = 2;
1088
    /// }
1089
    /// ```
1090
0
    pub fn get_mut(&mut self) -> &mut T {
1091
0
        self.c.get_mut()
1092
0
    }
1093
1094
    /// Consumes the lock, returning the underlying data.
1095
0
    pub fn into_inner(self) -> T
1096
0
    where
1097
0
        T: Sized,
1098
    {
1099
0
        self.c.into_inner()
1100
0
    }
1101
}
1102
1103
impl<T> From<T> for RwLock<T> {
1104
0
    fn from(s: T) -> Self {
1105
0
        Self::new(s)
1106
0
    }
1107
}
1108
1109
impl<T> Default for RwLock<T>
1110
where
1111
    T: Default,
1112
{
1113
0
    fn default() -> Self {
1114
0
        Self::new(T::default())
1115
0
    }
1116
}
1117
1118
impl<T: ?Sized> std::fmt::Debug for RwLock<T>
1119
where
1120
    T: std::fmt::Debug,
1121
{
1122
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1123
0
        let mut d = f.debug_struct("RwLock");
1124
0
        match self.try_read() {
1125
0
            Ok(inner) => d.field("data", &&*inner),
1126
0
            Err(_) => d.field("data", &format_args!("<locked>")),
1127
        };
1128
0
        d.finish()
1129
0
    }
1130
}