use crate::enums::solver_mode::SolverMode;
use crate::functions::make_type_check_limits::make_type_check_limits;
use crate::records::build_queue_work_state::{BuildQueueWorkState, Task};
use crate::records::frontend::Frontend;
use crate::records::frontend_options::FrontendOptions;
use crate::type_aliases::module_name_file_resolver::ModuleName;
use alloc::boxed::Box;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::cell::Cell;
use luaur_common::macros::luau_assert::LUAU_ASSERT;
use luaur_common::records::dense_hash_set::DenseHashSet;
use std::collections::HashMap;
use std::sync::{Condvar, Mutex};
fn task_to_fn(task: Task) -> Box<dyn Fn()> {
let cell = Cell::new(Some(task));
Box::new(move || {
if let Some(t) = cell.take() {
t();
}
})
}
struct ExecutorWrapper {
inner: Box<dyn Fn(Vec<Box<dyn Fn()>>)>,
}
unsafe impl Send for ExecutorWrapper {}
unsafe impl Sync for ExecutorWrapper {}
impl ExecutorWrapper {
fn run(&self, tasks: Vec<Task>) {
let adapted: Vec<Box<dyn Fn()>> = tasks.into_iter().map(task_to_fn).collect();
(self.inner)(adapted);
}
}
impl Frontend {
pub fn check_queued_modules(
&mut self,
option_override: Option<FrontendOptions>,
execute_tasks: Box<dyn Fn(Vec<Box<dyn Fn()>>)>,
progress: Box<dyn Fn(usize, usize) -> bool>,
) -> Vec<ModuleName> {
let mut frontend_options = option_override.unwrap_or_else(|| self.options.clone());
if self.get_luau_solver_mode() == SolverMode::New {
frontend_options.for_autocomplete = false;
}
let curr_module_queue: Vec<ModuleName> = core::mem::take(&mut self.module_queue);
let mut seen: DenseHashSet<ModuleName> = DenseHashSet::new(ModuleName::default());
let state = Arc::new(BuildQueueWorkState {
execute_task_deprecated: None,
execute_tasks: None,
build_queue_items: Vec::new(),
mtx: Mutex::new(()),
cv: Condvar::new(),
ready_queue_items: Vec::new(),
processing: 0,
remaining: 0,
});
let state_ptr = Arc::as_ptr(&state) as *mut BuildQueueWorkState;
for name in &curr_module_queue {
if seen.contains(name) {
continue;
}
if !self.is_dirty(name, frontend_options.for_autocomplete) {
seen.insert(name.clone());
continue;
}
let mut queue: Vec<ModuleName> = Vec::new();
let cycle_detected = self.parse_graph(
&mut queue,
name,
&make_type_check_limits(&frontend_options),
frontend_options.for_autocomplete,
);
{
let bqi = unsafe { &mut (*state_ptr).build_queue_items };
self.add_build_queue_items(
bqi,
&queue,
cycle_detected,
&mut seen,
&frontend_options,
);
}
}
{
let bqi = unsafe { &(*state_ptr).build_queue_items };
if bqi.is_empty() {
return Vec::new();
}
}
let mut module_name_to_queue: HashMap<ModuleName, usize> = HashMap::new();
{
let bqi = unsafe { &(*state_ptr).build_queue_items };
for i in 0..bqi.len() {
module_name_to_queue.insert(bqi[i].name.clone(), i);
}
}
let executor = ExecutorWrapper {
inner: execute_tasks,
};
unsafe {
(*state_ptr).execute_tasks = Some(Box::new(move |tasks: Vec<Task>| {
executor.run(tasks);
}));
}
{
let len = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
bqi.len()
};
unsafe {
(*state_ptr).remaining = len;
}
}
{
let count = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
bqi.len()
};
for i in 0..count {
let deps: Vec<ModuleName> = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
bqi[i].source_node.require_set.iter().cloned().collect()
};
for dep in deps {
if let Some(node) = self.source_nodes.get(&dep) {
if node.has_dirty_module(frontend_options.for_autocomplete) {
let dep_pos = module_name_to_queue[&dep];
let bqi = unsafe { &mut (*state_ptr).build_queue_items };
bqi[i].dirty_dependencies += 1;
bqi[dep_pos].reverse_deps.push(i);
}
}
}
}
}
let mut next_items: Vec<usize> = Vec::new();
{
let bqi = unsafe { &(*state_ptr).build_queue_items };
for i in 0..bqi.len() {
if bqi[i].dirty_dependencies == 0 {
next_items.push(i);
}
}
}
if !next_items.is_empty() {
self.send_queue_item_tasks(state.clone(), core::mem::take(&mut next_items));
}
if unsafe { (*state_ptr).processing } == 0 {
self.send_queue_cycle_item_task(state.clone());
}
let mut item_with_exception: Option<usize> = None;
let mut cancelled = false;
while unsafe { (*state_ptr).remaining } != 0 {
{
let mtx = unsafe { &(*state_ptr).mtx };
let cv = unsafe { &(*state_ptr).cv };
let guard = mtx.lock().unwrap();
let _guard = cv
.wait_while(guard, |_| {
let ready = unsafe { &(*state_ptr).ready_queue_items };
ready.is_empty()
})
.unwrap();
let ready: Vec<usize> = {
let r = unsafe { &(*state_ptr).ready_queue_items };
r.clone()
};
for i in ready.iter().copied() {
let (has_exception, is_cancelled) = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
(bqi[i].exception.is_some(), bqi[i].module.cancelled)
};
if has_exception {
item_with_exception = Some(i);
}
if is_cancelled {
cancelled = true;
}
if item_with_exception.is_some() || cancelled {
break;
}
{
let bqi = unsafe { &(*state_ptr).build_queue_items };
let item_ref = &bqi[i];
self.record_item_result(item_ref);
}
let reverse_deps: Vec<usize> = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
bqi[i].reverse_deps.clone()
};
for reverse_dep in reverse_deps {
let bqi = unsafe { &mut (*state_ptr).build_queue_items };
LUAU_ASSERT!(bqi[reverse_dep].dirty_dependencies != 0);
bqi[reverse_dep].dirty_dependencies -= 1;
if !bqi[reverse_dep].processing && bqi[reverse_dep].dirty_dependencies == 0
{
next_items.push(reverse_dep);
}
}
}
{
let ready_len = {
let r = unsafe { &(*state_ptr).ready_queue_items };
r.len()
};
unsafe {
LUAU_ASSERT!((*state_ptr).processing >= ready_len);
(*state_ptr).processing -= ready_len;
LUAU_ASSERT!((*state_ptr).remaining >= ready_len);
(*state_ptr).remaining -= ready_len;
}
let r = unsafe { &mut (*state_ptr).ready_queue_items };
r.clear();
}
}
{
let total = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
bqi.len()
};
let done = total - unsafe { (*state_ptr).remaining };
if !progress(done, total) {
cancelled = true;
}
}
if !next_items.is_empty() {
self.send_queue_item_tasks(state.clone(), core::mem::take(&mut next_items));
}
if unsafe { (*state_ptr).processing } == 0 {
if cancelled {
return Vec::new();
}
if let Some(idx) = item_with_exception {
let bqi = unsafe { &(*state_ptr).build_queue_items };
let item_ref = &bqi[idx];
self.record_item_result(item_ref);
}
}
if unsafe { (*state_ptr).remaining } != 0 && unsafe { (*state_ptr).processing } == 0 {
self.send_queue_cycle_item_task(state.clone());
}
}
let mut checked_modules: Vec<ModuleName> = Vec::new();
{
let count = {
let bqi = unsafe { &(*state_ptr).build_queue_items };
bqi.len()
};
checked_modules.reserve(count);
for i in 0..count {
let bqi = unsafe { &mut (*state_ptr).build_queue_items };
checked_modules.push(core::mem::take(&mut bqi[i].name));
}
}
checked_modules
}
}