use crate::compiler::{CompilationResult, CompilationTask, CompilationUnit};
use std::{
collections::VecDeque,
sync::{Arc, Mutex, RwLock},
thread,
};
use typescript_types::{TsError, TsValue};
use num_cpus;
#[derive(Debug, Clone)]
pub struct TaskQueue {
queue: Arc<Mutex<VecDeque<CompilationTask>>>,
completed_tasks: Arc<Mutex<usize>>,
total_tasks: Arc<Mutex<usize>>,
}
impl TaskQueue {
pub fn new() -> Self {
Self {
queue: Arc::new(Mutex::new(VecDeque::new())),
completed_tasks: Arc::new(Mutex::new(0)),
total_tasks: Arc::new(Mutex::new(0)),
}
}
pub fn add_task(&self, task: CompilationTask) {
let mut queue = self.queue.lock().unwrap();
queue.push_back(task);
let mut total_tasks = self.total_tasks.lock().unwrap();
*total_tasks += 1;
}
pub fn get_task(&self) -> Option<CompilationTask> {
let mut queue = self.queue.lock().unwrap();
queue.pop_front()
}
pub fn mark_task_completed(&self) {
let mut completed_tasks = self.completed_tasks.lock().unwrap();
*completed_tasks += 1;
}
pub fn get_completed_tasks(&self) -> usize {
*self.completed_tasks.lock().unwrap()
}
pub fn get_total_tasks(&self) -> usize {
*self.total_tasks.lock().unwrap()
}
pub fn is_all_tasks_completed(&self) -> bool {
*self.completed_tasks.lock().unwrap() == *self.total_tasks.lock().unwrap()
}
pub fn is_empty(&self) -> bool {
self.queue.lock().unwrap().is_empty()
}
}
#[derive(Debug, Clone)]
pub struct ResultCollector {
results: Arc<RwLock<Vec<(String, CompilationResult<TsValue>)>>>,
}
impl ResultCollector {
pub fn new() -> Self {
Self { results: Arc::new(RwLock::new(Vec::new())) }
}
pub fn add_result(&self, task_id: String, result: CompilationResult<TsValue>) {
let mut results = self.results.write().unwrap();
results.push((task_id, result));
}
pub fn get_results(&self) -> Vec<(String, CompilationResult<TsValue>)> {
self.results.read().unwrap().clone()
}
pub fn get_result_count(&self) -> usize {
self.results.read().unwrap().len()
}
}
#[derive(Clone)]
pub struct ParallelCompiler {
thread_count: usize,
task_queue: TaskQueue,
result_collector: ResultCollector,
compile_fn: Arc<dyn Fn(&CompilationUnit) -> CompilationResult<TsValue> + Send + Sync>,
}
impl std::fmt::Debug for ParallelCompiler {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParallelCompiler")
.field("thread_count", &self.thread_count)
.field("task_queue", &self.task_queue)
.field("result_collector", &self.result_collector)
.field("compile_fn", &"Fn(&CompilationUnit) -> CompilationResult<TsValue>")
.finish()
}
}
impl ParallelCompiler {
pub fn new(
thread_count: usize,
compile_fn: Arc<dyn Fn(&CompilationUnit) -> CompilationResult<TsValue> + Send + Sync>,
) -> Self {
Self { thread_count, task_queue: TaskQueue::new(), result_collector: ResultCollector::new(), compile_fn }
}
pub fn add_task(&self, task: CompilationTask) {
self.task_queue.add_task(task);
}
pub fn execute(&self) -> Vec<(String, CompilationResult<TsValue>)> {
let mut handles = Vec::new();
for _ in 0..self.thread_count {
let task_queue = self.task_queue.clone();
let result_collector = self.result_collector.clone();
let compile_fn = self.compile_fn.clone();
let handle = thread::spawn(move || {
while !task_queue.is_all_tasks_completed() {
if let Some(task) = task_queue.get_task() {
let result = (compile_fn)(&task.unit);
result_collector.add_result(task.id, result);
task_queue.mark_task_completed();
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
self.result_collector.get_results()
}
pub fn get_task_queue(&self) -> &TaskQueue {
&self.task_queue
}
pub fn get_result_collector(&self) -> &ResultCollector {
&self.result_collector
}
}
impl Default for ParallelCompiler {
fn default() -> Self {
Self::new(
num_cpus::get(),
Arc::new(|_unit| CompilationResult::Error(TsError::Other("Default compile function".to_string()))),
)
}
}