Coverage Report

Created: 2026-04-12 06:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.51.1/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 more 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!(
275
0
            max_reads <= MAX_READS,
276
0
            "a RwLock may not be created with more than {MAX_READS} readers"
277
        );
278
279
        #[cfg(all(tokio_unstable, feature = "tracing"))]
280
        let resource_span = {
281
            let location = std::panic::Location::caller();
282
283
            let resource_span = tracing::trace_span!(
284
                parent: None,
285
                "runtime.resource",
286
                concrete_type = "RwLock",
287
                kind = "Sync",
288
                loc.file = location.file(),
289
                loc.line = location.line(),
290
                loc.col = location.column(),
291
            );
292
293
            resource_span.in_scope(|| {
294
                tracing::trace!(
295
                    target: "runtime::resource::state_update",
296
                    max_readers = max_reads,
297
                );
298
299
                tracing::trace!(
300
                    target: "runtime::resource::state_update",
301
                    write_locked = false,
302
                );
303
304
                tracing::trace!(
305
                    target: "runtime::resource::state_update",
306
                    current_readers = 0,
307
                );
308
            });
309
310
            resource_span
311
        };
312
313
        #[cfg(all(tokio_unstable, feature = "tracing"))]
314
        let s = resource_span.in_scope(|| Semaphore::new(max_reads as usize));
315
316
        #[cfg(any(not(tokio_unstable), not(feature = "tracing")))]
317
0
        let s = Semaphore::new(max_reads as usize);
318
319
0
        RwLock {
320
0
            mr: max_reads,
321
0
            c: UnsafeCell::new(value),
322
0
            s,
323
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
324
0
            resource_span,
325
0
        }
326
0
    }
327
328
    /// Creates a new instance of an `RwLock<T>` which is unlocked.
329
    ///
330
    /// When using the `tracing` [unstable feature], a `RwLock` created with
331
    /// `const_new` will not be instrumented. As such, it will not be visible
332
    /// in [`tokio-console`]. Instead, [`RwLock::new`] should be used to create
333
    /// an instrumented object if that is needed.
334
    ///
335
    /// # Examples
336
    ///
337
    /// ```
338
    /// use tokio::sync::RwLock;
339
    ///
340
    /// static LOCK: RwLock<i32> = RwLock::const_new(5);
341
    /// ```
342
    ///
343
    /// [`tokio-console`]: https://github.com/tokio-rs/console
344
    /// [unstable feature]: crate#unstable-features
345
    #[cfg(not(all(loom, test)))]
346
0
    pub const fn const_new(value: T) -> RwLock<T>
347
0
    where
348
0
        T: Sized,
349
    {
350
0
        RwLock {
351
0
            mr: MAX_READS,
352
0
            c: UnsafeCell::new(value),
353
0
            s: Semaphore::const_new(MAX_READS as usize),
354
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
355
0
            resource_span: tracing::Span::none(),
356
0
        }
357
0
    }
358
359
    /// Creates a new instance of an `RwLock<T>` which is unlocked
360
    /// and allows a maximum of `max_reads` concurrent readers.
361
    ///
362
    /// # Examples
363
    ///
364
    /// ```
365
    /// use tokio::sync::RwLock;
366
    ///
367
    /// static LOCK: RwLock<i32> = RwLock::const_with_max_readers(5, 1024);
368
    /// ```
369
    #[cfg(not(all(loom, test)))]
370
0
    pub const fn const_with_max_readers(value: T, max_reads: u32) -> RwLock<T>
371
0
    where
372
0
        T: Sized,
373
    {
374
0
        assert!(max_reads <= MAX_READS);
375
376
0
        RwLock {
377
0
            mr: max_reads,
378
0
            c: UnsafeCell::new(value),
379
0
            s: Semaphore::const_new(max_reads as usize),
380
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
381
0
            resource_span: tracing::Span::none(),
382
0
        }
383
0
    }
384
385
    /// Locks this `RwLock` with shared read access, causing the current task
386
    /// to yield until the lock has been acquired.
387
    ///
388
    /// The calling task will yield until there are no writers which hold the
389
    /// lock. There may be other readers inside the lock when the task resumes.
390
    ///
391
    /// Note that under the priority policy of [`RwLock`], read locks are not
392
    /// granted until prior write locks, to prevent starvation. Therefore
393
    /// deadlock may occur if a read lock is held by the current task, a write
394
    /// lock attempt is made, and then a subsequent read lock attempt is made
395
    /// by the current task.
396
    ///
397
    /// Returns an RAII guard which will drop this read access of the `RwLock`
398
    /// when dropped.
399
    ///
400
    /// # Cancel safety
401
    ///
402
    /// This method uses a queue to fairly distribute locks in the order they
403
    /// were requested. Cancelling a call to `read` makes you lose your place in
404
    /// the queue.
405
    ///
406
    /// # Examples
407
    ///
408
    /// ```
409
    /// use std::sync::Arc;
410
    /// use tokio::sync::RwLock;
411
    ///
412
    /// # #[tokio::main(flavor = "current_thread")]
413
    /// # async fn main() {
414
    /// let lock = Arc::new(RwLock::new(1));
415
    /// let c_lock = lock.clone();
416
    ///
417
    /// let n = lock.read().await;
418
    /// assert_eq!(*n, 1);
419
    ///
420
    /// tokio::spawn(async move {
421
    ///     // While main has an active read lock, we acquire one too.
422
    ///     let r = c_lock.read().await;
423
    ///     assert_eq!(*r, 1);
424
    /// }).await.expect("The spawned task has panicked");
425
    ///
426
    /// // Drop the guard after the spawned task finishes.
427
    /// drop(n);
428
    /// # }
429
    /// ```
430
0
    pub async fn read(&self) -> RwLockReadGuard<'_, T> {
431
0
        let acquire_fut = async {
432
0
            self.s.acquire(1).await.unwrap_or_else(|_| {
433
                // The semaphore was closed. but, we never explicitly close it, and we have a
434
                // handle to it through the Arc, which means that this can never happen.
435
0
                unreachable!()
436
            });
437
438
0
            RwLockReadGuard {
439
0
                s: &self.s,
440
0
                data: self.c.get(),
441
0
                marker: PhantomData,
442
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
443
0
                resource_span: self.resource_span.clone(),
444
0
            }
445
0
        };
446
447
        #[cfg(all(tokio_unstable, feature = "tracing"))]
448
        let acquire_fut = trace::async_op(
449
            move || acquire_fut,
450
            self.resource_span.clone(),
451
            "RwLock::read",
452
            "poll",
453
            false,
454
        );
455
456
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
457
0
        let guard = acquire_fut.await;
458
459
        #[cfg(all(tokio_unstable, feature = "tracing"))]
460
        self.resource_span.in_scope(|| {
461
            tracing::trace!(
462
            target: "runtime::resource::state_update",
463
            current_readers = 1,
464
            current_readers.op = "add",
465
            )
466
        });
467
468
0
        guard
469
0
    }
470
471
    /// Blockingly locks this `RwLock` with shared read access.
472
    ///
473
    /// This method is intended for use cases where you
474
    /// need to use this rwlock in asynchronous code as well as in synchronous code.
475
    ///
476
    /// Returns an RAII guard which will drop the read access of this `RwLock` when dropped.
477
    ///
478
    /// # Panics
479
    ///
480
    /// This function panics if called within an asynchronous execution context.
481
    ///
482
    ///   - If you find yourself in an asynchronous execution context and needing
483
    ///     to call some (synchronous) function which performs one of these
484
    ///     `blocking_` operations, then consider wrapping that call inside
485
    ///     [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
486
    ///     (or [`block_in_place()`][crate::task::block_in_place]).
487
    ///
488
    /// # Examples
489
    ///
490
    /// ```
491
    /// # #[cfg(not(target_family = "wasm"))]
492
    /// # {
493
    /// use std::sync::Arc;
494
    /// use tokio::sync::RwLock;
495
    ///
496
    /// #[tokio::main]
497
    /// async fn main() {
498
    ///     let rwlock = Arc::new(RwLock::new(1));
499
    ///     let mut write_lock = rwlock.write().await;
500
    ///
501
    ///     let blocking_task = tokio::task::spawn_blocking({
502
    ///         let rwlock = Arc::clone(&rwlock);
503
    ///         move || {
504
    ///             // This shall block until the `write_lock` is released.
505
    ///             let read_lock = rwlock.blocking_read();
506
    ///             assert_eq!(*read_lock, 0);
507
    ///         }
508
    ///     });
509
    ///
510
    ///     *write_lock -= 1;
511
    ///     drop(write_lock); // release the lock.
512
    ///
513
    ///     // Await the completion of the blocking task.
514
    ///     blocking_task.await.unwrap();
515
    ///
516
    ///     // Assert uncontended.
517
    ///     assert!(rwlock.try_write().is_ok());
518
    /// }
519
    /// # }
520
    /// ```
521
    #[track_caller]
522
    #[cfg(feature = "sync")]
523
0
    pub fn blocking_read(&self) -> RwLockReadGuard<'_, T> {
524
0
        crate::future::block_on(self.read())
525
0
    }
526
527
    /// Locks this `RwLock` with shared read access, causing the current task
528
    /// to yield until the lock has been acquired.
529
    ///
530
    /// The calling task will yield until there are no writers which hold the
531
    /// lock. There may be other readers inside the lock when the task resumes.
532
    ///
533
    /// This method is identical to [`RwLock::read`], except that the returned
534
    /// guard references the `RwLock` with an [`Arc`] rather than by borrowing
535
    /// it. Therefore, the `RwLock` must be wrapped in an `Arc` to call this
536
    /// method, and the guard will live for the `'static` lifetime, as it keeps
537
    /// the `RwLock` alive by holding an `Arc`.
538
    ///
539
    /// Note that under the priority policy of [`RwLock`], read locks are not
540
    /// granted until prior write locks, to prevent starvation. Therefore
541
    /// deadlock may occur if a read lock is held by the current task, a write
542
    /// lock attempt is made, and then a subsequent read lock attempt is made
543
    /// by the current task.
544
    ///
545
    /// Returns an RAII guard which will drop this read access of the `RwLock`
546
    /// when dropped.
547
    ///
548
    /// # Cancel safety
549
    ///
550
    /// This method uses a queue to fairly distribute locks in the order they
551
    /// were requested. Cancelling a call to `read_owned` makes you lose your
552
    /// place in the queue.
553
    ///
554
    /// # Examples
555
    ///
556
    /// ```
557
    /// use std::sync::Arc;
558
    /// use tokio::sync::RwLock;
559
    ///
560
    /// # #[tokio::main(flavor = "current_thread")]
561
    /// # async fn main() {
562
    /// let lock = Arc::new(RwLock::new(1));
563
    /// let c_lock = lock.clone();
564
    ///
565
    /// let n = lock.read_owned().await;
566
    /// assert_eq!(*n, 1);
567
    ///
568
    /// tokio::spawn(async move {
569
    ///     // While main has an active read lock, we acquire one too.
570
    ///     let r = c_lock.read_owned().await;
571
    ///     assert_eq!(*r, 1);
572
    /// }).await.expect("The spawned task has panicked");
573
    ///
574
    /// // Drop the guard after the spawned task finishes.
575
    /// drop(n);
576
    ///}
577
    /// ```
578
0
    pub async fn read_owned(self: Arc<Self>) -> OwnedRwLockReadGuard<T> {
579
        #[cfg(all(tokio_unstable, feature = "tracing"))]
580
        let resource_span = self.resource_span.clone();
581
582
0
        let acquire_fut = async {
583
0
            self.s.acquire(1).await.unwrap_or_else(|_| {
584
                // The semaphore was closed. but, we never explicitly close it, and we have a
585
                // handle to it through the Arc, which means that this can never happen.
586
0
                unreachable!()
587
            });
588
589
0
            OwnedRwLockReadGuard {
590
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
591
0
                resource_span: self.resource_span.clone(),
592
0
                data: self.c.get(),
593
0
                lock: self,
594
0
                _p: PhantomData,
595
0
            }
596
0
        };
597
598
        #[cfg(all(tokio_unstable, feature = "tracing"))]
599
        let acquire_fut = trace::async_op(
600
            move || acquire_fut,
601
            resource_span,
602
            "RwLock::read_owned",
603
            "poll",
604
            false,
605
        );
606
607
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
608
0
        let guard = acquire_fut.await;
609
610
        #[cfg(all(tokio_unstable, feature = "tracing"))]
611
        guard.resource_span.in_scope(|| {
612
            tracing::trace!(
613
            target: "runtime::resource::state_update",
614
            current_readers = 1,
615
            current_readers.op = "add",
616
            )
617
        });
618
619
0
        guard
620
0
    }
621
622
    /// Attempts to acquire this `RwLock` with shared read access.
623
    ///
624
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
625
    /// Otherwise, an RAII guard is returned which will release read access
626
    /// when dropped.
627
    ///
628
    /// [`TryLockError`]: TryLockError
629
    ///
630
    /// # Examples
631
    ///
632
    /// ```
633
    /// use std::sync::Arc;
634
    /// use tokio::sync::RwLock;
635
    ///
636
    /// # #[tokio::main(flavor = "current_thread")]
637
    /// # async fn main() {
638
    /// let lock = Arc::new(RwLock::new(1));
639
    /// let c_lock = lock.clone();
640
    ///
641
    /// let v = lock.try_read().unwrap();
642
    /// assert_eq!(*v, 1);
643
    ///
644
    /// tokio::spawn(async move {
645
    ///     // While main has an active read lock, we acquire one too.
646
    ///     let n = c_lock.read().await;
647
    ///     assert_eq!(*n, 1);
648
    /// }).await.expect("The spawned task has panicked");
649
    ///
650
    /// // Drop the guard when spawned task finishes.
651
    /// drop(v);
652
    /// # }
653
    /// ```
654
0
    pub fn try_read(&self) -> Result<RwLockReadGuard<'_, T>, TryLockError> {
655
0
        match self.s.try_acquire(1) {
656
0
            Ok(permit) => permit,
657
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
658
0
            Err(TryAcquireError::Closed) => unreachable!(),
659
        }
660
661
0
        let guard = RwLockReadGuard {
662
0
            s: &self.s,
663
0
            data: self.c.get(),
664
0
            marker: marker::PhantomData,
665
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
666
0
            resource_span: self.resource_span.clone(),
667
0
        };
668
669
        #[cfg(all(tokio_unstable, feature = "tracing"))]
670
        self.resource_span.in_scope(|| {
671
            tracing::trace!(
672
            target: "runtime::resource::state_update",
673
            current_readers = 1,
674
            current_readers.op = "add",
675
            )
676
        });
677
678
0
        Ok(guard)
679
0
    }
680
681
    /// Attempts to acquire this `RwLock` with shared read access.
682
    ///
683
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
684
    /// Otherwise, an RAII guard is returned which will release read access
685
    /// when dropped.
686
    ///
687
    /// This method is identical to [`RwLock::try_read`], except that the
688
    /// returned guard references the `RwLock` with an [`Arc`] rather than by
689
    /// borrowing it. Therefore, the `RwLock` must be wrapped in an `Arc` to
690
    /// call this method, and the guard will live for the `'static` lifetime,
691
    /// as it keeps the `RwLock` alive by holding an `Arc`.
692
    ///
693
    /// [`TryLockError`]: TryLockError
694
    ///
695
    /// # Examples
696
    ///
697
    /// ```
698
    /// use std::sync::Arc;
699
    /// use tokio::sync::RwLock;
700
    ///
701
    /// # #[tokio::main(flavor = "current_thread")]
702
    /// # async fn main() {
703
    /// let lock = Arc::new(RwLock::new(1));
704
    /// let c_lock = lock.clone();
705
    ///
706
    /// let v = lock.try_read_owned().unwrap();
707
    /// assert_eq!(*v, 1);
708
    ///
709
    /// tokio::spawn(async move {
710
    ///     // While main has an active read lock, we acquire one too.
711
    ///     let n = c_lock.read_owned().await;
712
    ///     assert_eq!(*n, 1);
713
    /// }).await.expect("The spawned task has panicked");
714
    ///
715
    /// // Drop the guard when spawned task finishes.
716
    /// drop(v);
717
    /// # }
718
    /// ```
719
0
    pub fn try_read_owned(self: Arc<Self>) -> Result<OwnedRwLockReadGuard<T>, TryLockError> {
720
0
        match self.s.try_acquire(1) {
721
0
            Ok(permit) => permit,
722
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
723
0
            Err(TryAcquireError::Closed) => unreachable!(),
724
        }
725
726
0
        let guard = OwnedRwLockReadGuard {
727
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
728
0
            resource_span: self.resource_span.clone(),
729
0
            data: self.c.get(),
730
0
            lock: self,
731
0
            _p: PhantomData,
732
0
        };
733
734
        #[cfg(all(tokio_unstable, feature = "tracing"))]
735
        guard.resource_span.in_scope(|| {
736
            tracing::trace!(
737
            target: "runtime::resource::state_update",
738
            current_readers = 1,
739
            current_readers.op = "add",
740
            )
741
        });
742
743
0
        Ok(guard)
744
0
    }
745
746
    /// Locks this `RwLock` with exclusive write access, causing the current
747
    /// task to yield until the lock has been acquired.
748
    ///
749
    /// The calling task will yield while other writers or readers currently
750
    /// have access to the lock.
751
    ///
752
    /// Returns an RAII guard which will drop the write access of this `RwLock`
753
    /// when dropped.
754
    ///
755
    /// # Cancel safety
756
    ///
757
    /// This method uses a queue to fairly distribute locks in the order they
758
    /// were requested. Cancelling a call to `write` makes you lose your place
759
    /// in the queue.
760
    ///
761
    /// # Examples
762
    ///
763
    /// ```
764
    /// use tokio::sync::RwLock;
765
    ///
766
    /// # #[tokio::main(flavor = "current_thread")]
767
    /// # async fn main() {
768
    /// let lock = RwLock::new(1);
769
    ///
770
    /// let mut n = lock.write().await;
771
    /// *n = 2;
772
    /// # }
773
    /// ```
774
0
    pub async fn write(&self) -> RwLockWriteGuard<'_, T> {
775
0
        let acquire_fut = async {
776
0
            self.s.acquire(self.mr as usize).await.unwrap_or_else(|_| {
777
                // The semaphore was closed. but, we never explicitly close it, and we have a
778
                // handle to it through the Arc, which means that this can never happen.
779
0
                unreachable!()
780
            });
781
782
0
            RwLockWriteGuard {
783
0
                permits_acquired: self.mr,
784
0
                s: &self.s,
785
0
                data: self.c.get(),
786
0
                marker: marker::PhantomData,
787
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
788
0
                resource_span: self.resource_span.clone(),
789
0
            }
790
0
        };
791
792
        #[cfg(all(tokio_unstable, feature = "tracing"))]
793
        let acquire_fut = trace::async_op(
794
            move || acquire_fut,
795
            self.resource_span.clone(),
796
            "RwLock::write",
797
            "poll",
798
            false,
799
        );
800
801
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
802
0
        let guard = acquire_fut.await;
803
804
        #[cfg(all(tokio_unstable, feature = "tracing"))]
805
        self.resource_span.in_scope(|| {
806
            tracing::trace!(
807
            target: "runtime::resource::state_update",
808
            write_locked = true,
809
            write_locked.op = "override",
810
            )
811
        });
812
813
0
        guard
814
0
    }
815
816
    /// Blockingly locks this `RwLock` with exclusive write access.
817
    ///
818
    /// This method is intended for use cases where you
819
    /// need to use this rwlock in asynchronous code as well as in synchronous code.
820
    ///
821
    /// Returns an RAII guard which will drop the write access of this `RwLock` when dropped.
822
    ///
823
    /// # Panics
824
    ///
825
    /// This function panics if called within an asynchronous execution context.
826
    ///
827
    ///   - If you find yourself in an asynchronous execution context and needing
828
    ///     to call some (synchronous) function which performs one of these
829
    ///     `blocking_` operations, then consider wrapping that call inside
830
    ///     [`spawn_blocking()`][crate::runtime::Handle::spawn_blocking]
831
    ///     (or [`block_in_place()`][crate::task::block_in_place]).
832
    ///
833
    /// # Examples
834
    ///
835
    /// ```
836
    /// # #[cfg(not(target_family = "wasm"))]
837
    /// # {
838
    /// use std::sync::Arc;
839
    /// use tokio::{sync::RwLock};
840
    ///
841
    /// #[tokio::main]
842
    /// async fn main() {
843
    ///     let rwlock =  Arc::new(RwLock::new(1));
844
    ///     let read_lock = rwlock.read().await;
845
    ///
846
    ///     let blocking_task = tokio::task::spawn_blocking({
847
    ///         let rwlock = Arc::clone(&rwlock);
848
    ///         move || {
849
    ///             // This shall block until the `read_lock` is released.
850
    ///             let mut write_lock = rwlock.blocking_write();
851
    ///             *write_lock = 2;
852
    ///         }
853
    ///     });
854
    ///
855
    ///     assert_eq!(*read_lock, 1);
856
    ///     // Release the last outstanding read lock.
857
    ///     drop(read_lock);
858
    ///
859
    ///     // Await the completion of the blocking task.
860
    ///     blocking_task.await.unwrap();
861
    ///
862
    ///     // Assert uncontended.
863
    ///     let read_lock = rwlock.try_read().unwrap();
864
    ///     assert_eq!(*read_lock, 2);
865
    /// }
866
    /// # }
867
    /// ```
868
    #[track_caller]
869
    #[cfg(feature = "sync")]
870
0
    pub fn blocking_write(&self) -> RwLockWriteGuard<'_, T> {
871
0
        crate::future::block_on(self.write())
872
0
    }
873
874
    /// Locks this `RwLock` with exclusive write access, causing the current
875
    /// task to yield until the lock has been acquired.
876
    ///
877
    /// The calling task will yield while other writers or readers currently
878
    /// have access to the lock.
879
    ///
880
    /// This method is identical to [`RwLock::write`], except that the returned
881
    /// guard references the `RwLock` with an [`Arc`] rather than by borrowing
882
    /// it. Therefore, the `RwLock` must be wrapped in an `Arc` to call this
883
    /// method, and the guard will live for the `'static` lifetime, as it keeps
884
    /// the `RwLock` alive by holding an `Arc`.
885
    ///
886
    /// Returns an RAII guard which will drop the write access of this `RwLock`
887
    /// when dropped.
888
    ///
889
    /// # Cancel safety
890
    ///
891
    /// This method uses a queue to fairly distribute locks in the order they
892
    /// were requested. Cancelling a call to `write_owned` makes you lose your
893
    /// place in the queue.
894
    ///
895
    /// # Examples
896
    ///
897
    /// ```
898
    /// use std::sync::Arc;
899
    /// use tokio::sync::RwLock;
900
    ///
901
    /// # #[tokio::main(flavor = "current_thread")]
902
    /// # async fn main() {
903
    /// let lock = Arc::new(RwLock::new(1));
904
    ///
905
    /// let mut n = lock.write_owned().await;
906
    /// *n = 2;
907
    ///}
908
    /// ```
909
0
    pub async fn write_owned(self: Arc<Self>) -> OwnedRwLockWriteGuard<T> {
910
        #[cfg(all(tokio_unstable, feature = "tracing"))]
911
        let resource_span = self.resource_span.clone();
912
913
0
        let acquire_fut = async {
914
0
            self.s.acquire(self.mr as usize).await.unwrap_or_else(|_| {
915
                // The semaphore was closed. but, we never explicitly close it, and we have a
916
                // handle to it through the Arc, which means that this can never happen.
917
0
                unreachable!()
918
            });
919
920
0
            OwnedRwLockWriteGuard {
921
0
                #[cfg(all(tokio_unstable, feature = "tracing"))]
922
0
                resource_span: self.resource_span.clone(),
923
0
                permits_acquired: self.mr,
924
0
                data: self.c.get(),
925
0
                lock: self,
926
0
                _p: PhantomData,
927
0
            }
928
0
        };
929
930
        #[cfg(all(tokio_unstable, feature = "tracing"))]
931
        let acquire_fut = trace::async_op(
932
            move || acquire_fut,
933
            resource_span,
934
            "RwLock::write_owned",
935
            "poll",
936
            false,
937
        );
938
939
        #[allow(clippy::let_and_return)] // this lint triggers when disabling tracing
940
0
        let guard = acquire_fut.await;
941
942
        #[cfg(all(tokio_unstable, feature = "tracing"))]
943
        guard.resource_span.in_scope(|| {
944
            tracing::trace!(
945
            target: "runtime::resource::state_update",
946
            write_locked = true,
947
            write_locked.op = "override",
948
            )
949
        });
950
951
0
        guard
952
0
    }
953
954
    /// Attempts to acquire this `RwLock` with exclusive write access.
955
    ///
956
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
957
    /// Otherwise, an RAII guard is returned which will release write access
958
    /// when dropped.
959
    ///
960
    /// [`TryLockError`]: TryLockError
961
    ///
962
    /// # Examples
963
    ///
964
    /// ```
965
    /// use tokio::sync::RwLock;
966
    ///
967
    /// # #[tokio::main(flavor = "current_thread")]
968
    /// # async fn main() {
969
    /// let rw = RwLock::new(1);
970
    ///
971
    /// let v = rw.read().await;
972
    /// assert_eq!(*v, 1);
973
    ///
974
    /// assert!(rw.try_write().is_err());
975
    /// # }
976
    /// ```
977
0
    pub fn try_write(&self) -> Result<RwLockWriteGuard<'_, T>, TryLockError> {
978
0
        match self.s.try_acquire(self.mr as usize) {
979
0
            Ok(permit) => permit,
980
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
981
0
            Err(TryAcquireError::Closed) => unreachable!(),
982
        }
983
984
0
        let guard = RwLockWriteGuard {
985
0
            permits_acquired: self.mr,
986
0
            s: &self.s,
987
0
            data: self.c.get(),
988
0
            marker: marker::PhantomData,
989
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
990
0
            resource_span: self.resource_span.clone(),
991
0
        };
992
993
        #[cfg(all(tokio_unstable, feature = "tracing"))]
994
        self.resource_span.in_scope(|| {
995
            tracing::trace!(
996
            target: "runtime::resource::state_update",
997
            write_locked = true,
998
            write_locked.op = "override",
999
            )
1000
        });
1001
1002
0
        Ok(guard)
1003
0
    }
1004
1005
    /// Attempts to acquire this `RwLock` with exclusive write access.
1006
    ///
1007
    /// If the access couldn't be acquired immediately, returns [`TryLockError`].
1008
    /// Otherwise, an RAII guard is returned which will release write access
1009
    /// when dropped.
1010
    ///
1011
    /// This method is identical to [`RwLock::try_write`], except that the
1012
    /// returned guard references the `RwLock` with an [`Arc`] rather than by
1013
    /// borrowing it. Therefore, the `RwLock` must be wrapped in an `Arc` to
1014
    /// call this method, and the guard will live for the `'static` lifetime,
1015
    /// as it keeps the `RwLock` alive by holding an `Arc`.
1016
    ///
1017
    /// [`TryLockError`]: TryLockError
1018
    ///
1019
    /// # Examples
1020
    ///
1021
    /// ```
1022
    /// use std::sync::Arc;
1023
    /// use tokio::sync::RwLock;
1024
    ///
1025
    /// # #[tokio::main(flavor = "current_thread")]
1026
    /// # async fn main() {
1027
    /// let rw = Arc::new(RwLock::new(1));
1028
    ///
1029
    /// let v = Arc::clone(&rw).read_owned().await;
1030
    /// assert_eq!(*v, 1);
1031
    ///
1032
    /// assert!(rw.try_write_owned().is_err());
1033
    /// # }
1034
    /// ```
1035
0
    pub fn try_write_owned(self: Arc<Self>) -> Result<OwnedRwLockWriteGuard<T>, TryLockError> {
1036
0
        match self.s.try_acquire(self.mr as usize) {
1037
0
            Ok(permit) => permit,
1038
0
            Err(TryAcquireError::NoPermits) => return Err(TryLockError(())),
1039
0
            Err(TryAcquireError::Closed) => unreachable!(),
1040
        }
1041
1042
0
        let guard = OwnedRwLockWriteGuard {
1043
0
            #[cfg(all(tokio_unstable, feature = "tracing"))]
1044
0
            resource_span: self.resource_span.clone(),
1045
0
            permits_acquired: self.mr,
1046
0
            data: self.c.get(),
1047
0
            lock: self,
1048
0
            _p: PhantomData,
1049
0
        };
1050
1051
        #[cfg(all(tokio_unstable, feature = "tracing"))]
1052
        guard.resource_span.in_scope(|| {
1053
            tracing::trace!(
1054
            target: "runtime::resource::state_update",
1055
            write_locked = true,
1056
            write_locked.op = "override",
1057
            )
1058
        });
1059
1060
0
        Ok(guard)
1061
0
    }
1062
1063
    /// Returns a mutable reference to the underlying data.
1064
    ///
1065
    /// Since this call borrows the `RwLock` mutably, no actual locking needs to
1066
    /// take place -- the mutable borrow statically guarantees no locks exist.
1067
    ///
1068
    /// # Examples
1069
    ///
1070
    /// ```
1071
    /// use tokio::sync::RwLock;
1072
    ///
1073
    /// fn main() {
1074
    ///     let mut lock = RwLock::new(1);
1075
    ///
1076
    ///     let n = lock.get_mut();
1077
    ///     *n = 2;
1078
    /// }
1079
    /// ```
1080
0
    pub fn get_mut(&mut self) -> &mut T {
1081
0
        self.c.get_mut()
1082
0
    }
1083
1084
    /// Consumes the lock, returning the underlying data.
1085
0
    pub fn into_inner(self) -> T
1086
0
    where
1087
0
        T: Sized,
1088
    {
1089
0
        self.c.into_inner()
1090
0
    }
1091
}
1092
1093
impl<T> From<T> for RwLock<T> {
1094
0
    fn from(s: T) -> Self {
1095
0
        Self::new(s)
1096
0
    }
1097
}
1098
1099
impl<T> Default for RwLock<T>
1100
where
1101
    T: Default,
1102
{
1103
0
    fn default() -> Self {
1104
0
        Self::new(T::default())
1105
0
    }
1106
}
1107
1108
impl<T: ?Sized> std::fmt::Debug for RwLock<T>
1109
where
1110
    T: std::fmt::Debug,
1111
{
1112
0
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1113
0
        let mut d = f.debug_struct("RwLock");
1114
0
        match self.try_read() {
1115
0
            Ok(inner) => d.field("data", &&*inner),
1116
0
            Err(_) => d.field("data", &format_args!("<locked>")),
1117
        };
1118
0
        d.finish()
1119
0
    }
1120
}