mirror of
https://github.com/Start9Labs/patch-db.git
synced 2026-03-26 10:21:53 +00:00
builds again, tests underway
This commit is contained in:
committed by
Aiden McClelland
parent
09697a3c5a
commit
ebc666302b
@@ -7,7 +7,7 @@ use crate::{
|
|||||||
LockSet,
|
LockSet,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use imbl::{ordset, OrdMap, OrdSet};
|
use imbl::{ordmap, ordset, OrdMap, OrdSet};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
@@ -217,15 +217,14 @@ fn kill_deadlocked(request_queue: &mut VecDeque<(Request, OrdSet<HandleId>)>, tr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deadlock_scan<'a>(queue: &'a VecDeque<(Request, OrdSet<HandleId>)>) -> Vec<&'a Request> {
|
pub(super) fn deadlock_scan<'a>(
|
||||||
|
queue: &'a VecDeque<(Request, OrdSet<HandleId>)>,
|
||||||
|
) -> Vec<&'a Request> {
|
||||||
let (wait_map, mut req_map) = queue
|
let (wait_map, mut req_map) = queue
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(req, set)| ((&req.lock_info.handle_id, set, req)))
|
.map(|(req, set)| ((&req.lock_info.handle_id, set, req)))
|
||||||
.fold(
|
.fold(
|
||||||
(
|
(ordmap! {}, ordmap! {}),
|
||||||
OrdMap::<&'a HandleId, &'a OrdSet<HandleId>>::new(),
|
|
||||||
OrdMap::<&'a HandleId, &'a Request>::new(),
|
|
||||||
),
|
|
||||||
|(mut wmap, mut rmap), (id, wset, req)| {
|
|(mut wmap, mut rmap), (id, wset, req)| {
|
||||||
(
|
(
|
||||||
{
|
{
|
||||||
@@ -239,25 +238,6 @@ fn deadlock_scan<'a>(queue: &'a VecDeque<(Request, OrdSet<HandleId>)>) -> Vec<&'
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
fn path_to<'a>(
|
|
||||||
graph: &OrdMap<&'a HandleId, &'a OrdSet<HandleId>>,
|
|
||||||
root: &'a HandleId,
|
|
||||||
node: &'a HandleId,
|
|
||||||
) -> OrdSet<&'a HandleId> {
|
|
||||||
if node == root {
|
|
||||||
return ordset![root];
|
|
||||||
}
|
|
||||||
match graph.get(node) {
|
|
||||||
None => ordset![],
|
|
||||||
Some(s) => s
|
|
||||||
.iter()
|
|
||||||
.find_map(|h| Some(path_to(graph, root, h)).filter(|s| s.is_empty()))
|
|
||||||
.map_or(ordset![], |mut s| {
|
|
||||||
s.insert(node);
|
|
||||||
s
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (root, wait_set) in wait_map.iter() {
|
for (root, wait_set) in wait_map.iter() {
|
||||||
let cycle = wait_set
|
let cycle = wait_set
|
||||||
.iter()
|
.iter()
|
||||||
@@ -276,3 +256,23 @@ fn deadlock_scan<'a>(queue: &'a VecDeque<(Request, OrdSet<HandleId>)>) -> Vec<&'
|
|||||||
}
|
}
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(super) fn path_to<'a>(
|
||||||
|
graph: &OrdMap<&'a HandleId, &'a OrdSet<HandleId>>,
|
||||||
|
root: &'a HandleId,
|
||||||
|
node: &'a HandleId,
|
||||||
|
) -> OrdSet<&'a HandleId> {
|
||||||
|
if node == root {
|
||||||
|
return ordset![root];
|
||||||
|
}
|
||||||
|
match graph.get(node) {
|
||||||
|
None => ordset![],
|
||||||
|
Some(s) => s
|
||||||
|
.iter()
|
||||||
|
.find_map(|h| Some(path_to(graph, root, h)).filter(|s| s.is_empty()))
|
||||||
|
.map_or(ordset![], |mut s| {
|
||||||
|
s.insert(node);
|
||||||
|
s
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ mod bookkeeper;
|
|||||||
mod log_utils;
|
mod log_utils;
|
||||||
mod natural;
|
mod natural;
|
||||||
mod order_enforcer;
|
mod order_enforcer;
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod proptest;
|
||||||
mod trie;
|
mod trie;
|
||||||
|
|
||||||
use imbl::{ordmap, ordset, OrdMap, OrdSet};
|
use imbl::{ordmap, ordset, OrdMap, OrdSet};
|
||||||
@@ -66,23 +68,6 @@ impl Locker {
|
|||||||
ptr: JsonPointer,
|
ptr: JsonPointer,
|
||||||
lock_type: LockType,
|
lock_type: LockType,
|
||||||
) -> Result<Guard, LockError> {
|
) -> Result<Guard, LockError> {
|
||||||
// Local Definitions
|
|
||||||
struct CancelGuard {
|
|
||||||
lock_info: Option<LockInfo>,
|
|
||||||
channel: Option<oneshot::Sender<LockInfo>>,
|
|
||||||
recv: oneshot::Receiver<Result<Guard, LockError>>,
|
|
||||||
}
|
|
||||||
impl Drop for CancelGuard {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
if let (Some(lock_info), Some(channel)) =
|
|
||||||
(self.lock_info.take(), self.channel.take())
|
|
||||||
{
|
|
||||||
self.recv.close();
|
|
||||||
let _ = channel.send(lock_info);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pertinent Logic
|
// Pertinent Logic
|
||||||
let lock_info = LockInfo {
|
let lock_info = LockInfo {
|
||||||
handle_id,
|
handle_id,
|
||||||
@@ -107,6 +92,20 @@ impl Locker {
|
|||||||
cancel_guard.channel.take();
|
cancel_guard.channel.take();
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
} // Local Definitions
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct CancelGuard {
|
||||||
|
lock_info: Option<LockInfo>,
|
||||||
|
channel: Option<oneshot::Sender<LockInfo>>,
|
||||||
|
recv: oneshot::Receiver<Result<Guard, LockError>>,
|
||||||
|
}
|
||||||
|
impl Drop for CancelGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let (Some(lock_info), Some(channel)) = (self.lock_info.take(), self.channel.take()) {
|
||||||
|
self.recv.close();
|
||||||
|
let _ = channel.send(lock_info);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
|||||||
@@ -1,30 +1,31 @@
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod proptest {
|
mod tests {
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use json_ptr::JsonPointer;
|
use json_ptr::JsonPointer;
|
||||||
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
use crate::handle::HandleId;
|
use crate::handle::HandleId;
|
||||||
use crate::locker::{Guard, LockType};
|
use crate::locker::{CancelGuard, Guard, LockInfo, LockType, Request};
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
|
|
||||||
enum Action {
|
// enum Action {
|
||||||
Acquire {
|
// Acquire {
|
||||||
lock_type: LockType,
|
// lock_type: LockType,
|
||||||
ptr: JsonPointer,
|
// ptr: JsonPointer,
|
||||||
},
|
// },
|
||||||
Release(JsonPointer),
|
// Release(JsonPointer),
|
||||||
}
|
// }
|
||||||
|
|
||||||
struct Session {
|
// struct Session {
|
||||||
// session id
|
// // session id
|
||||||
id: HandleId,
|
// id: HandleId,
|
||||||
// list of actions and whether or not they have been completed (await returns before test freezes state)
|
// // list of actions and whether or not they have been completed (await returns before test freezes state)
|
||||||
actions: Vec<(Action, bool)>,
|
// actions: Vec<(Action, bool)>,
|
||||||
// lookup table for (json pointers, action indices) -> release action
|
// // lookup table for (json pointers, action indices) -> release action
|
||||||
guard: HashMap<(JsonPointer, usize), Guard>,
|
// guard: HashMap<(JsonPointer, usize), Guard>,
|
||||||
}
|
// }
|
||||||
type Traversal = Vec<usize>;
|
// type Traversal = Vec<usize>;
|
||||||
|
|
||||||
// randomly select the type of lock we are requesting
|
// randomly select the type of lock we are requesting
|
||||||
fn arb_lock_type() -> BoxedStrategy<LockType> {
|
fn arb_lock_type() -> BoxedStrategy<LockType> {
|
||||||
@@ -36,20 +37,63 @@ mod proptest {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
// randomly generate session ids
|
|
||||||
prop_compose! {
|
prop_compose! {
|
||||||
fn arb_handle_id()(i in any::<u64>()) -> HandleId {
|
fn arb_handle_id(n: u64)(x in 0..n) -> HandleId {
|
||||||
HandleId {
|
HandleId {
|
||||||
id: i,
|
id: x,
|
||||||
|
#[cfg(feature = "trace")]
|
||||||
|
trace: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// the test trie we will be using is an arbitrarily deep binary tree of L and R paths. This will be sufficient to
|
fn arb_json_ptr(max_size: usize) -> BoxedStrategy<JsonPointer> {
|
||||||
// test sibling concurrency and won't introduce any unnecessary complexity to the suite. This is the primitive fork
|
(1..max_size)
|
||||||
// choice generator
|
.prop_flat_map(|n| {
|
||||||
fn arb_json_fork_choice() -> BoxedStrategy<char> {
|
let s = proptest::bool::ANY.prop_map(|b| if b { "b" } else { "a" });
|
||||||
prop_oneof![Just('L'), Just('R'),].boxed()
|
proptest::collection::vec_deque(s, n).prop_flat_map(|v| {
|
||||||
|
let mut ptr = JsonPointer::default();
|
||||||
|
for seg in v {
|
||||||
|
ptr.push_end(seg);
|
||||||
|
}
|
||||||
|
Just(ptr)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arb_lock_info(session_bound: u64, ptr_max_size: usize) -> BoxedStrategy<LockInfo> {
|
||||||
|
arb_handle_id(session_bound)
|
||||||
|
.prop_flat_map(move |handle_id| {
|
||||||
|
arb_json_ptr(ptr_max_size).prop_flat_map(move |ptr| {
|
||||||
|
let handle_id = handle_id.clone();
|
||||||
|
arb_lock_type().prop_map(move |ty| LockInfo {
|
||||||
|
handle_id: handle_id.clone(),
|
||||||
|
ty,
|
||||||
|
ptr: ptr.clone(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
|
}
|
||||||
|
|
||||||
|
prop_compose! {
|
||||||
|
fn arb_request(session_bound: u64, ptr_max_size: usize)(li in arb_lock_info(session_bound, ptr_max_size)) -> (Request, CancelGuard) {
|
||||||
|
let (cancel_send, cancel_recv) = oneshot::channel();
|
||||||
|
let (guard_send, guard_recv) = oneshot::channel();
|
||||||
|
let r = Request {
|
||||||
|
lock_info: li.clone(),
|
||||||
|
cancel: Some(cancel_recv),
|
||||||
|
completion: guard_send,
|
||||||
|
|
||||||
|
};
|
||||||
|
let c = CancelGuard {
|
||||||
|
lock_info: Some(li),
|
||||||
|
channel: Some(cancel_send),
|
||||||
|
recv: guard_recv,
|
||||||
|
};
|
||||||
|
(r, c)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
|
|||||||
Reference in New Issue
Block a user