works to the best of my knowledge

This commit is contained in:
Keagan McClelland
2022-01-04 18:22:20 -07:00
parent 077da29fb1
commit 1dab59afdf
4 changed files with 74 additions and 30 deletions

View File

@@ -1,7 +1,5 @@
use tokio::sync::{ use tokio::sync::mpsc::{self, UnboundedReceiver};
mpsc::{self, UnboundedReceiver}, use tokio::sync::oneshot;
oneshot,
};
use super::{LockInfo, Request}; use super::{LockInfo, Request};
@@ -20,13 +18,16 @@ pub(super) struct ActionMux {
inbound_request_queue: InboundRequestQueue, inbound_request_queue: InboundRequestQueue,
unlock_receivers: Vec<oneshot::Receiver<LockInfo>>, unlock_receivers: Vec<oneshot::Receiver<LockInfo>>,
cancellation_receivers: Vec<oneshot::Receiver<LockInfo>>, cancellation_receivers: Vec<oneshot::Receiver<LockInfo>>,
_dummy_senders: Vec<oneshot::Sender<LockInfo>>,
} }
impl ActionMux { impl ActionMux {
pub fn new(inbound_receiver: UnboundedReceiver<Request>) -> Self { pub fn new(inbound_receiver: UnboundedReceiver<Request>) -> Self {
// futures::future::select_all will panic if the list is empty // futures::future::select_all will panic if the list is empty
// instead we want it to block forever by adding a channel that will never recv // instead we want it to block forever by adding a channel that will never recv
let unlock_receivers = vec![oneshot::channel().1]; let (unlock_dummy_send, unlock_dummy_recv) = oneshot::channel();
let cancellation_receivers = vec![oneshot::channel().1]; let unlock_receivers = vec![unlock_dummy_recv];
let (cancel_dummy_send, cancel_dummy_recv) = oneshot::channel();
let cancellation_receivers = vec![cancel_dummy_recv];
ActionMux { ActionMux {
inbound_request_queue: InboundRequestQueue { inbound_request_queue: InboundRequestQueue {
recv: inbound_receiver, recv: inbound_receiver,
@@ -34,6 +35,7 @@ impl ActionMux {
}, },
unlock_receivers, unlock_receivers,
cancellation_receivers, cancellation_receivers,
_dummy_senders: vec![unlock_dummy_send, cancel_dummy_send],
} }
} }
pub async fn get_action(&mut self) -> Option<Action> { pub async fn get_action(&mut self) -> Option<Action> {

View File

@@ -53,6 +53,7 @@ impl LockBookkeeper {
if let Some(hot_seat) = hot_seat { if let Some(hot_seat) = hot_seat {
self.deferred_request_queue.push_front(hot_seat); self.deferred_request_queue.push_front(hot_seat);
kill_deadlocked(&mut self.deferred_request_queue, &mut self.trie);
} }
Ok(res) Ok(res)
} }
@@ -126,6 +127,7 @@ impl LockBookkeeper {
} }
if let Some(hot_seat) = hot_seat { if let Some(hot_seat) = hot_seat {
self.deferred_request_queue.push_front(hot_seat); self.deferred_request_queue.push_front(hot_seat);
kill_deadlocked(&mut self.deferred_request_queue, &mut self.trie);
} }
new_unlock_receivers new_unlock_receivers
} }
@@ -157,7 +159,6 @@ fn process_new_req(
} }
request_queue.push_back((req, ordset![])); request_queue.push_back((req, ordset![]));
kill_deadlocked(request_queue, &*trie);
None None
} }
// otherwise we try and service it immediately, only pushing to the queue if it fails // otherwise we try and service it immediately, only pushing to the queue if it fails
@@ -179,7 +180,6 @@ fn process_new_req(
} }
request_queue.push_back((req, blocking_sessions)); request_queue.push_back((req, blocking_sessions));
kill_deadlocked(request_queue, &*trie);
None None
} }
}, },
@@ -189,8 +189,7 @@ fn process_new_req(
fn kill_deadlocked(request_queue: &mut VecDeque<(Request, OrdSet<HandleId>)>, trie: &LockTrie) { fn kill_deadlocked(request_queue: &mut VecDeque<(Request, OrdSet<HandleId>)>, trie: &LockTrie) {
// TODO optimize this, it is unlikely that we are anywhere close to as efficient as we can be here. // TODO optimize this, it is unlikely that we are anywhere close to as efficient as we can be here.
let deadlocked_reqs = deadlock_scan(request_queue); let deadlocked_reqs = deadlock_scan(request_queue);
let last = request_queue.back().unwrap(); if !deadlocked_reqs.is_empty() {
if !deadlocked_reqs.is_empty() && deadlocked_reqs.iter().any(|r| std::ptr::eq(*r, &last.0)) {
let locks_waiting = LockSet( let locks_waiting = LockSet(
deadlocked_reqs deadlocked_reqs
.iter() .iter()
@@ -204,23 +203,20 @@ fn kill_deadlocked(request_queue: &mut VecDeque<(Request, OrdSet<HandleId>)>, tr
locks_held: LockSet(trie.subtree_lock_info()), locks_held: LockSet(trie.subtree_lock_info()),
}; };
request_queue.pop_back().unwrap().0.reject(err); let mut indices_to_remove = Vec::with_capacity(deadlocked_reqs.len());
for (i, (req, _)) in request_queue.iter().enumerate() {
// This commented logic is for if we want to kill the whole cycle, rather than the most recent addition if deadlocked_reqs.iter().any(|r| std::ptr::eq(*r, req)) {
// let mut indices_to_remove = Vec::with_capacity(deadlocked_reqs.len()); indices_to_remove.push(i)
// for (i, (req, _)) in request_queue.iter().enumerate() { }
// if deadlocked_reqs.iter().any(|r| std::ptr::eq(*r, req)) { }
// indices_to_remove.push(i) let old = std::mem::take(request_queue);
// } for (i, (r, s)) in old.into_iter().enumerate() {
// } if indices_to_remove.contains(&i) {
// let old = std::mem::take(request_queue); r.reject(err.clone())
// for (i, (r, s)) in old.into_iter().enumerate() { } else {
// if indices_to_remove.contains(&i) { request_queue.push_back((r, s))
// r.reject(err.clone()) }
// } else { }
// request_queue.push_back((r, s))
// }
// }
} }
} }

View File

@@ -76,6 +76,7 @@ impl Locker {
ptr, ptr,
ty: lock_type, ty: lock_type,
}; };
println!("{}", &lock_info);
let (send, recv) = oneshot::channel(); let (send, recv) = oneshot::channel();
let (cancel_send, cancel_recv) = oneshot::channel(); let (cancel_send, cancel_recv) = oneshot::channel();
let mut cancel_guard = CancelGuard { let mut cancel_guard = CancelGuard {
@@ -249,7 +250,7 @@ pub enum LockError {
first: JsonPointer, first: JsonPointer,
second: JsonPointer, second: JsonPointer,
}, },
#[error("Deadlock Detected: Locks Held = {locks_held}, Locks Waiting = {locks_waiting}")] #[error("Deadlock Detected:\nLocks Held =\n{locks_held},\nLocks Waiting =\n{locks_waiting}")]
DeadlockDetected { DeadlockDetected {
locks_held: LockSet, locks_held: LockSet,
locks_waiting: LockSet, locks_waiting: LockSet,

View File

@@ -11,7 +11,8 @@ mod tests {
use crate::handle::HandleId; use crate::handle::HandleId;
use crate::locker::bookkeeper::{deadlock_scan, path_to}; use crate::locker::bookkeeper::{deadlock_scan, path_to};
use crate::locker::{CancelGuard, Guard, LockInfo, LockType, Request}; use crate::locker::{CancelGuard, Guard, LockError, LockInfo, LockType, Request};
use crate::Locker;
// enum Action { // enum Action {
// Acquire { // Acquire {
@@ -157,7 +158,7 @@ mod tests {
req.0, req.0,
ordset![HandleId { ordset![HandleId {
id: dep, id: dep,
#[cfg(feature = "tracing-error")] #[cfg(feature = "trace")]
trace: None trace: None
}], }],
)); ));
@@ -225,6 +226,50 @@ mod tests {
}); });
} }
#[tokio::test]
async fn deadlock_kill_live() {
let locker = Locker::new();
let s0 = HandleId {
id: 0,
#[cfg(feature = "trace")]
trace: None,
};
let s1 = HandleId {
id: 1,
#[cfg(feature = "trace")]
trace: None,
};
let x = locker
.lock(s0.clone(), "/a/b".parse().unwrap(), LockType::Read)
.await;
assert!(x.is_ok());
let y = locker
.lock(s1.clone(), "/a/b".parse().unwrap(), LockType::Read)
.await;
assert!(y.is_ok());
let x = tokio::select! {
r0 = locker.lock(s0, "/a/b".parse().unwrap(), LockType::Write) => r0,
r1 = locker.lock(s1, "/a/b".parse().unwrap(), LockType::Write) => r1,
};
match x {
Ok(g) => {
println!("wat");
drop(g);
assert!(false);
}
Err(e) => match e {
LockError::DeadlockDetected { .. } => {
println!("{}", e);
}
_ => {
println!("{}", e);
#[cfg(not(feature = "unstable"))]
assert!(false);
}
},
}
}
proptest! { proptest! {
#[test] #[test]
fn zero_or_one_write_lock_per_traversal(x in 0..10) { fn zero_or_one_write_lock_per_traversal(x in 0..10) {