mirror of
https://github.com/tokio-rs/tokio.git
synced 2026-08-18 00:00:09 +02:00
watch: document recursive borrow deadlock (#4360)
Under the hood, the watch channel uses a RwLock to implement reading (borrow) and writing (send). This may cause a deadlock if a user has concurrent borrows on the same thread. This is most likely to occur due to a recursive borrow. This PR adds documentation to describe the deadlock so that future users of the watch channel will be aware.
This commit is contained in:
@@ -91,6 +91,23 @@ pub struct Sender<T> {
|
||||
/// Outstanding borrows hold a read lock on the inner value. This means that
|
||||
/// long lived borrows could cause the produce half to block. It is recommended
|
||||
/// to keep the borrow as short lived as possible.
|
||||
///
|
||||
/// The priority policy of the lock is dependent on the underlying lock
|
||||
/// implementation, and this type does not guarantee that any particular policy
|
||||
/// will be used. In particular, a producer which is waiting to acquire the lock
|
||||
/// in `send` might or might not block concurrent calls to `borrow`, e.g.:
|
||||
///
|
||||
/// <details><summary>Potential deadlock example</summary>
|
||||
///
|
||||
/// ```text
|
||||
/// // Task 1 (on thread A) | // Task 2 (on thread B)
|
||||
/// let _ref1 = rx.borrow(); |
|
||||
/// | // will block
|
||||
/// | let _ = tx.send(());
|
||||
/// // may deadlock |
|
||||
/// let _ref2 = rx.borrow(); |
|
||||
/// ```
|
||||
/// </details>
|
||||
#[derive(Debug)]
|
||||
pub struct Ref<'a, T> {
|
||||
inner: RwLockReadGuard<'a, T>,
|
||||
@@ -285,6 +302,23 @@ impl<T> Receiver<T> {
|
||||
/// could cause the send half to block. It is recommended to keep the borrow
|
||||
/// as short lived as possible.
|
||||
///
|
||||
/// The priority policy of the lock is dependent on the underlying lock
|
||||
/// implementation, and this type does not guarantee that any particular policy
|
||||
/// will be used. In particular, a producer which is waiting to acquire the lock
|
||||
/// in `send` might or might not block concurrent calls to `borrow`, e.g.:
|
||||
///
|
||||
/// <details><summary>Potential deadlock example</summary>
|
||||
///
|
||||
/// ```text
|
||||
/// // Task 1 (on thread A) | // Task 2 (on thread B)
|
||||
/// let _ref1 = rx.borrow(); |
|
||||
/// | // will block
|
||||
/// | let _ = tx.send(());
|
||||
/// // may deadlock |
|
||||
/// let _ref2 = rx.borrow(); |
|
||||
/// ```
|
||||
/// </details>
|
||||
///
|
||||
/// [`changed`]: Receiver::changed
|
||||
///
|
||||
/// # Examples
|
||||
@@ -311,6 +345,23 @@ impl<T> Receiver<T> {
|
||||
/// could cause the send half to block. It is recommended to keep the borrow
|
||||
/// as short lived as possible.
|
||||
///
|
||||
/// The priority policy of the lock is dependent on the underlying lock
|
||||
/// implementation, and this type does not guarantee that any particular policy
|
||||
/// will be used. In particular, a producer which is waiting to acquire the lock
|
||||
/// in `send` might or might not block concurrent calls to `borrow`, e.g.:
|
||||
///
|
||||
/// <details><summary>Potential deadlock example</summary>
|
||||
///
|
||||
/// ```text
|
||||
/// // Task 1 (on thread A) | // Task 2 (on thread B)
|
||||
/// let _ref1 = rx1.borrow_and_update(); |
|
||||
/// | // will block
|
||||
/// | let _ = tx.send(());
|
||||
/// // may deadlock |
|
||||
/// let _ref2 = rx2.borrow_and_update(); |
|
||||
/// ```
|
||||
/// </details>
|
||||
///
|
||||
/// [`changed`]: Receiver::changed
|
||||
pub fn borrow_and_update(&mut self) -> Ref<'_, T> {
|
||||
let inner = self.shared.value.read().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user