use dashmap::DashMap;
use crate::waiter::Waiter;
use std::fmt::{self, Debug};
use std::hash::Hash;
use std::io;
use std::time::Duration;
#[derive(Debug)]
pub struct WaiterGuard<'a, K: Hash + Eq + 'a, T: 'a> {
owner: &'a WaiterMap<K, T>,
id: K,
}
impl<'a, K: Hash + Eq + Debug, T> WaiterGuard<'a, K, T> {
pub fn wait_rsp<D: Into<Option<Duration>>>(&self, timeout: D) -> io::Result<T> {
self.owner.wait_rsp(&self.id, timeout.into())
}
}
impl<'a, K: Hash + Eq, T> Drop for WaiterGuard<'a, K, T> {
fn drop(&mut self) {
self.owner.del_waiter(&self.id);
}
}
pub struct WaiterMap<K, T> {
map: DashMap<K, Waiter<T>>,
}
impl<K: Hash + Eq, T> Debug for WaiterMap<K, T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "WaiterMap{{ ... }}")
}
}
impl<K: Hash + Eq, T> Default for WaiterMap<K, T> {
fn default() -> Self {
WaiterMap::new()
}
}
impl<K: Hash + Eq, T> WaiterMap<K, T> {
pub fn new() -> Self {
WaiterMap {
map: DashMap::new(),
}
}
pub fn new_waiter(&self, id: K) -> WaiterGuard<K, T>
where
K: Clone,
{
match self.map.insert(id.clone(), Waiter::new()) {
Some(_w) => panic!("waiter id already in use!"),
None => WaiterGuard { owner: self, id },
}
}
fn del_waiter(&self, id: &K) -> Option<Waiter<T>> {
self.map.remove(id).map(|v| v.1)
}
fn wait_rsp(&self, id: &K, timeout: Option<Duration>) -> io::Result<T>
where
K: Debug,
{
fn extend_lifetime<'a, T>(r: &T) -> &'a T {
unsafe { ::std::mem::transmute(r) }
}
let waiter = match self.map.get(id) {
Some(v) => extend_lifetime(&*v),
None => unreachable!("can't find id in waiter map!"),
};
waiter.wait_rsp(timeout)
}
pub fn set_rsp(&self, id: &K, rsp: T) -> Result<(), T>
where
K: Debug,
{
match self.map.get(id) {
Some(waiter) => {
waiter.set_rsp(rsp);
Ok(())
}
None => Err(rsp),
}
}
pub fn cancel_all(&mut self) {
self.map.iter().for_each(|waiter| waiter.cancel_wait());
}
}
#[cfg(test)]
mod tests {
use super::*;
use may::go;
#[test]
fn test_waiter_map() {
use std::sync::Arc;
let req_map = Arc::new(WaiterMap::<usize, usize>::new());
let req_map_1 = req_map.clone();
let key = 1234;
let waiter = req_map.new_waiter(key);
go!(move || req_map_1.set_rsp(&key, 100).ok());
let result = waiter.wait_rsp(None).unwrap();
assert_eq!(result, 100);
}
}