use std::{cell::UnsafeCell, marker::PhantomData, sync::{self, Mutex}, thread::{JoinHandle, spawn}};
use std::sync::Arc;
use sync::Condvar;
use super::{CodeExecutor, CodeGenerator, ThreadContext};
pub(crate) enum CompilationStatus {
Pending,
Done,
Error(String)
}
impl CompilationStatus {
pub(crate) fn is_pending(&self) -> bool {
matches!(self, Self::Pending)
}
}
#[derive(Clone, Debug)]
pub(crate) enum WorkerTask {
Idle,
Run(usize),
Stop
}
impl WorkerTask {
pub(crate) fn is_idle(&self) -> bool {
matches!(self, Self::Idle)
}
}
pub(crate) enum WorkerStatus {
Busy,
Idle,
Dead
}
impl WorkerStatus {
pub(crate) fn is_busy(&self) -> bool {
matches!(self, Self::Busy)
}
}
pub(crate) struct WorkerThread {
thread: JoinHandle<()>,
task_flag: Arc<(Mutex<WorkerTask>, Condvar)>,
compilation_flag: Arc<(Mutex<CompilationStatus>, Condvar)>,
status_flag: Arc<(Mutex<WorkerStatus>, Condvar)>,
_no_sync: PhantomData<UnsafeCell<()>> }
impl WorkerThread {
pub(crate) fn spawn(context: ThreadContext) -> Self {
let task_flag = Arc::new((Mutex::new(WorkerTask::Idle), Condvar::new()));
let task_flag_clone = task_flag.clone();
let compilation_flag = Arc::new((Mutex::new(CompilationStatus::Pending), Condvar::new()));
let compilation_flag_clone = compilation_flag.clone();
let status_flag = Arc::new((Mutex::new(WorkerStatus::Idle), Condvar::new()));
let status_flag_clone = status_flag.clone();
let thread = spawn(move || {
let context = context;
let task_flag = task_flag_clone;
let compilation_flag = compilation_flag_clone;
let status_flag = status_flag_clone;
let compilation_result = (|| {
let codegen = CodeGenerator::new(context)?;
CodeExecutor::new(codegen)
})();
let set_compilation_status = |status| {
let (compilation_lock, compilation_cvar) = &*compilation_flag;
let mut compilation_status = compilation_lock.lock().unwrap();
*compilation_status = status;
compilation_cvar.notify_all();
};
let mut executor = match compilation_result {
Ok(executor) => {
set_compilation_status(CompilationStatus::Done);
executor
},
Err(error) => {
set_compilation_status(CompilationStatus::Error(error.to_string()));
return;
}
};
loop {
let (task_lock, task_cvar) = &*task_flag;
let mut task = task_lock.lock().unwrap();
task = task_cvar.wait_while(task, |task| task.is_idle()).unwrap();
let (status_lock, status_cvar) = &*status_flag;
let mut status = status_lock.lock().unwrap();
*status = WorkerStatus::Busy;
std::mem::drop(status);
status_cvar.notify_all();
let tmp = task.clone();
*task = WorkerTask::Idle;
std::mem::drop(task);
task_cvar.notify_all();
let task = tmp;
match task {
WorkerTask::Idle => unreachable!(),
WorkerTask::Run(num) => {
for _ in 0..num {
executor.run()
}
}
WorkerTask::Stop => { break; }
};
let task = task_lock.lock().unwrap();
if task.is_idle() {
let (status_lock, status_cvar) = &*status_flag;
let mut status = status_lock.lock().unwrap();
*status = WorkerStatus::Idle;
std::mem::drop(status);
status_cvar.notify_all();
}
std::mem::drop(task);
};
let (status_lock, status_cvar) = &*status_flag;
let mut status = status_lock.lock().unwrap();
*status = WorkerStatus::Dead;
std::mem::drop(status);
status_cvar.notify_all();
});
Self {
thread,
task_flag,
compilation_flag,
status_flag,
_no_sync: PhantomData
}
}
pub(crate) fn wait_for_compilation(&self) {
let (compilation_lock, compilation_cvar) = &*self.compilation_flag;
let mut compilation_status = compilation_lock.lock().unwrap();
compilation_status = compilation_cvar.wait_while(compilation_status,
|compilation_status| compilation_status.is_pending()).unwrap();
match &*compilation_status {
CompilationStatus::Error(e) => panic!("{}", e),
CompilationStatus::Done => {}
CompilationStatus::Pending => unreachable!(),
}
}
pub(crate) fn run_step(&self) {
let (task_lock, task_cvar) = &*self.task_flag;
let mut task = task_lock.lock().unwrap();
*task = match *task {
WorkerTask::Idle => WorkerTask::Run(1),
WorkerTask::Run(num) => WorkerTask::Run(num+1),
WorkerTask::Stop => WorkerTask::Stop
};
task_cvar.notify_all();
}
pub(crate) fn wait(&self) {
let (task_lock, task_cvar) = &*self.task_flag;
let mut task = task_lock.lock().unwrap();
task = task_cvar.wait_while(task, |task| !task.is_idle()).unwrap();
std::mem::drop(task);
let (status_lock, status_cvar) = &*self.status_flag;
let mut status = status_lock.lock().unwrap();
status = status_cvar.wait_while(status, |status| status.is_busy()).unwrap();
std::mem::drop(status);
}
pub(crate) fn join(self) {
let (task_lock, task_cvar) = &*self.task_flag;
let mut task = task_lock.lock().unwrap();
*task = WorkerTask::Stop;
std::mem::drop(task);
task_cvar.notify_all();
self.thread.join().expect("Cannot join threads");
}
}