use crate::executors::executor::{Executor, ExecutorTask};
use crate::common::tsafe::TSafe;
use std::sync::{Mutex, Arc, Condvar};
use std::any::Any;
use std::collections::vec_deque::VecDeque;
use std::thread;
use std::time::Duration;
use rand::{Rng};
type Queue = TSafe<VecDeque<ExecutorTask>>;
#[derive(Clone)]
pub enum DistributionStrategy {
Random,
Round,
Load,
EventLoop
}
pub struct TaskOptions {
pub thread_id: Option<usize>
}
pub struct ThreadPinnedExecutor {
threads_count: usize,
distribution_strategy: DistributionStrategy,
queues: Vec<Queue>,
locks: Vec<Arc<Condvar>>,
stops: Vec<TSafe<bool>>,
rounds: usize
}
impl ThreadPinnedExecutor {
pub fn new() -> ThreadPinnedExecutor {
let cpu_count = num_cpus::get();
ThreadPinnedExecutor {
threads_count: cpu_count,
distribution_strategy: DistributionStrategy::Round,
locks: Vec::new(),
queues: Vec::new(),
stops: Vec::new(),
rounds: 0
}
}
pub fn set_threads_count(mut self, count: usize) -> Self {
self.threads_count = count;
self
}
pub fn get_threads_count(&self) -> usize {
self.threads_count
}
pub fn set_distribution_strategy(mut self, strategy: DistributionStrategy) -> Self {
self.distribution_strategy = strategy;
self
}
pub fn run(mut self) -> Self {
for i in 0..self.threads_count {
let stop = tsafe!(false);
let queue = tsafe!(VecDeque::new());
let cvar = Arc::new(Condvar::new());
let mutex = Mutex::new(false);
let _tid = i;
self.queues.push(queue.clone());
self.locks.push(cvar.clone());
self.stops.push(stop.clone());
thread::spawn(move || {
while *stop.lock().unwrap() == false {
let f: Option<ExecutorTask> = {
let mut q = queue.lock().unwrap();
if q.len() > 0 {
Some(q.pop_front().unwrap())
} else {
None
}
};
if f.is_some() {
f.unwrap()();
} else {
cvar.wait_timeout(mutex.lock().unwrap(), Duration::from_millis(1000));
}
}
let mut q = queue.lock().unwrap();
q.clear();
});
}
self
}
fn get_thread_id(&mut self, strategy: DistributionStrategy) -> usize {
match strategy {
DistributionStrategy::Load => {
let mut min = 1000000000;
let mut min_q = 0;
let mut qn = 0;
for q in self.queues.iter() {
let len = q.lock().unwrap().len();
if len < min {
min = len;
min_q = qn;
}
qn = qn + 1;
}
min_q
},
DistributionStrategy::Round => {
if self.rounds == self.threads_count - 1 {
self.rounds = 0;
} else {
self.rounds = self.rounds + 1;
}
self.rounds
},
DistributionStrategy::Random => {
rand::thread_rng().gen_range(0, self.threads_count - 1)
},
DistributionStrategy::EventLoop => {
0
}
}
}
}
impl Executor for ThreadPinnedExecutor {
fn execute(&mut self, f: ExecutorTask, options: Option<Box<Any>>) {
let thread_id = if options.is_some() {
let options = options.unwrap();
let options = options.downcast_ref::<TaskOptions>().unwrap();
if options.thread_id.is_some() {
options.thread_id.unwrap()
} else {
self.get_thread_id(self.distribution_strategy.clone())
}
} else {
self.get_thread_id(self.distribution_strategy.clone())
};
self.queues[thread_id].lock().unwrap().push_back(f);
self.locks[thread_id].notify_one();
}
fn stop(&mut self) {
for stop in self.stops.iter() {
*stop.lock().unwrap() = true;
}
for cvar in self.locks.iter() {
cvar.notify_all();
}
}
}