-
Notifications
You must be signed in to change notification settings - Fork 407
Fix (and test) threaded payment retries #2009
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9422370
6090d9e
1dc96cb
d986329
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,6 +14,8 @@ use std::sync::Condvar as StdCondvar; | |
|
||
use crate::prelude::HashMap; | ||
|
||
use super::{LockTestExt, LockHeldState}; | ||
|
||
#[cfg(feature = "backtrace")] | ||
use {crate::prelude::hash_map, backtrace::Backtrace, std::sync::Once}; | ||
|
||
|
@@ -168,6 +170,18 @@ impl LockMetadata { | |
fn pre_lock(this: &Arc<LockMetadata>) { Self::_pre_lock(this, false); } | ||
fn pre_read_lock(this: &Arc<LockMetadata>) -> bool { Self::_pre_lock(this, true) } | ||
|
||
fn held_by_thread(this: &Arc<LockMetadata>) -> LockHeldState { | ||
let mut res = LockHeldState::NotHeldByThread; | ||
LOCKS_HELD.with(|held| { | ||
for (locked_idx, _locked) in held.borrow().iter() { | ||
if *locked_idx == this.lock_idx { | ||
res = LockHeldState::HeldByThread; | ||
TheBlueMatt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
}); | ||
res | ||
} | ||
|
||
fn try_locked(this: &Arc<LockMetadata>) { | ||
LOCKS_HELD.with(|held| { | ||
// Since a try-lock will simply fail if the lock is held already, we do not | ||
|
@@ -248,6 +262,13 @@ impl<T> Mutex<T> { | |
} | ||
} | ||
|
||
impl <T> LockTestExt for Mutex<T> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More of a general question, but how come rust-lightning has its own synchronization primitives like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yea, we don't actually use our own custom locks, we always stub out to the std ones, except in two cases: a) in tests, we wrap them in all kinds of debug testing to ensure we don't have lockorder violations, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just took a better look at the locks and that makes more sense now 👍 |
||
#[inline] | ||
fn held_by_thread(&self) -> LockHeldState { | ||
LockMetadata::held_by_thread(&self.deps) | ||
} | ||
} | ||
|
||
pub struct RwLock<T: Sized> { | ||
inner: StdRwLock<T>, | ||
deps: Arc<LockMetadata>, | ||
|
@@ -332,4 +353,11 @@ impl<T> RwLock<T> { | |
} | ||
} | ||
|
||
impl <T> LockTestExt for RwLock<T> { | ||
#[inline] | ||
fn held_by_thread(&self) -> LockHeldState { | ||
LockMetadata::held_by_thread(&self.deps) | ||
} | ||
} | ||
|
||
pub type FairRwLock<T> = RwLock<T>; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,16 @@ | ||
#[allow(dead_code)] // Depending on the compilation flags some variants are never used | ||
#[derive(Debug, PartialEq, Eq)] | ||
pub(crate) enum LockHeldState { | ||
HeldByThread, | ||
NotHeldByThread, | ||
#[cfg(any(feature = "_bench_unstable", not(test)))] | ||
Unsupported, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think a comment explaining when a lock held state cannot be determined would be helpful There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Even better, I cfg-flagged it so its not even there for test builds. |
||
} | ||
|
||
pub(crate) trait LockTestExt { | ||
fn held_by_thread(&self) -> LockHeldState; | ||
} | ||
|
||
#[cfg(all(feature = "std", not(feature = "_bench_unstable"), test))] | ||
mod debug_sync; | ||
#[cfg(all(feature = "std", not(feature = "_bench_unstable"), test))] | ||
|
@@ -7,9 +20,22 @@ pub use debug_sync::*; | |
mod test_lockorder_checks; | ||
|
||
#[cfg(all(feature = "std", any(feature = "_bench_unstable", not(test))))] | ||
pub use ::std::sync::{Arc, Mutex, Condvar, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}; | ||
pub(crate) mod fairrwlock; | ||
#[cfg(all(feature = "std", any(feature = "_bench_unstable", not(test))))] | ||
pub use {std::sync::{Arc, Mutex, Condvar, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard}, fairrwlock::FairRwLock}; | ||
|
||
#[cfg(all(feature = "std", any(feature = "_bench_unstable", not(test))))] | ||
pub use crate::util::fairrwlock::FairRwLock; | ||
mod ext_impl { | ||
use super::*; | ||
impl<T> LockTestExt for Mutex<T> { | ||
#[inline] | ||
fn held_by_thread(&self) -> LockHeldState { LockHeldState::Unsupported } | ||
} | ||
impl<T> LockTestExt for RwLock<T> { | ||
#[inline] | ||
fn held_by_thread(&self) -> LockHeldState { LockHeldState::Unsupported } | ||
} | ||
} | ||
|
||
#[cfg(not(feature = "std"))] | ||
mod nostd_sync; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we return early if we fail to acquire the lock?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Eh? I mean, we could, but I don't want to bother adding more code than necessary (and we probably always want to at least run once, so we'd have to do the whole rigamarole that peer_handler does), and we really shouldnt have two threads calling this, at least as long as we only generate one PendingHTLCsForwardable event.