use std::collections::BinaryHeap;
use std::sync::{Condvar, Mutex};
use crate::Prioritized;
#[derive(Debug)]
pub struct SegmentedPriorityQueue<T> {
reader: Mutex<BinaryHeap<Prioritized<T>>>,
writer: Mutex<BinaryHeap<Prioritized<T>>>,
cv: Condvar,
}
impl<T> Default for SegmentedPriorityQueue<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> SegmentedPriorityQueue<T> {
pub fn new() -> SegmentedPriorityQueue<T> {
SegmentedPriorityQueue {
reader: Mutex::default(),
writer: Mutex::default(),
cv: Condvar::new(),
}
}
pub fn push(&self, t: T, priority: u64) {
let mut q = self.writer.lock().unwrap();
q.push(Prioritized { t, priority });
drop(q);
self.cv.notify_one();
}
pub fn pop(&self) -> T {
let mut q = self.reader.lock().unwrap();
while q.is_empty() {
let mut q2 = self.writer.lock().unwrap();
std::mem::swap(&mut *q, &mut *q2);
drop(q2);
if q.is_empty() {
q = self.cv.wait(q).unwrap();
}
}
q.pop().unwrap().t
}
}
#[test]
fn segmented_priority_queue_concurrent_pops() {
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::{Arc, Barrier};
const N_WRITES_PER_WRITER: u64 = 1_000_000;
const N_WRITERS: u64 = 10;
const N_WRITES: u64 = N_WRITERS * N_WRITES_PER_WRITER;
const N_READERS: u64 = 10;
let q = Arc::new(SegmentedPriorityQueue::new());
let barrier = Arc::new(Barrier::new(N_WRITERS.try_into().unwrap()));
let read = Arc::new(AtomicU64::new(0));
for i in 0..N_WRITERS {
let q = q.clone();
let barrier = barrier.clone();
std::thread::spawn(move || {
barrier.wait();
for j in 0..N_WRITES_PER_WRITER {
q.push(i * j, i);
}
});
}
for _ in 0..N_READERS {
let q = q.clone();
let read = read.clone();
std::thread::spawn(move || loop {
q.pop();
read.fetch_add(1, Ordering::SeqCst);
});
}
while read.load(Ordering::SeqCst) < N_WRITES {
std::hint::spin_loop();
}
}