Skip to content

Commit

Permalink
fix(lsp): cycle recovery panic issue & await context check
Browse files Browse the repository at this point in the history
- Avoid cycle recovery logic when cycle deps was detected by adding a thread local input cache for
input of `compile_dry_file`. This help lsp to be more efficient and avoid a bug(?) in salsa causing panic
after cycle recovery from time to time.
- Updated salsa to the latest version to fix changing input causing panic bug (salsa-rs/salsa#590).
- Added an async helper function `spawn` which can start the task without directly await operation. This function return a new task which can be await later to get the result.
- Update parser to support function def with inplicit return type. Those function's ret type will be treated as `void`
- Add check logic for await context, now awaiting in none async function will report error as expected
- Add finalizer support for gc, using gc finalizer to implicitly destroy the mutex (TODO: update condvar to use finalizer)
  • Loading branch information
Chronostasys committed Oct 23, 2024
1 parent fccc678 commit 4740c6e
Show file tree
Hide file tree
Showing 21 changed files with 230 additions and 53 deletions.
6 changes: 3 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion immix
17 changes: 9 additions & 8 deletions planglib/std/chan.pi
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pub struct Chan<T> {
buffer: Queue<T>;
count: u64;
capacity: u64;
mtx: *Mutex;
mtx: **MutexHandle;
condvar: *CondVar;
}
struct Node<T> {
Expand Down Expand Up @@ -47,30 +47,31 @@ pub fn channel<T>(sz: u64) Chan<T> {
ch.buffer = Queue<T>{};
ch.count = 0;
ch.capacity = sz;
create_mutex(&ch.mtx);
ch.mtx = &&MutexHandle{};
create_mutex(ch.mtx);
create_condvar(&ch.condvar);
return ch;
}
use std::io;
impl <S>Chan<S> {
pub fn send(s: S) void {
lock_mutex(self.mtx);
lock_mutex(*self.mtx);
while self.count == self.capacity {
condvar_wait(self.condvar, self.mtx);
condvar_wait(self.condvar, *self.mtx);
}
self.buffer.push(s);
// io::printi64ln(self.count as i64);
self.count = self.count + 1;
if self.count == 1 {
condvar_notify(self.condvar);
}
unlock_mutex(self.mtx);
unlock_mutex(*self.mtx);
return;
}
pub fn recv() S {
lock_mutex(self.mtx);
lock_mutex(*self.mtx);
while self.count == 0 {
condvar_wait(self.condvar, self.mtx);
condvar_wait(self.condvar, *self.mtx);
}
let s = self.buffer.pop();
self.count = self.count - 1;
Expand All @@ -79,7 +80,7 @@ impl <S>Chan<S> {
condvar_notify(self.condvar);
}

unlock_mutex(self.mtx);
unlock_mutex(*self.mtx);
return s;
}
pub fn len() u64 {
Expand Down
31 changes: 25 additions & 6 deletions planglib/std/mutex.pi
Original file line number Diff line number Diff line change
@@ -1,21 +1,40 @@
pub struct Mutex{}
pub struct MutexHandle{}

pub fn create_mutex(mutex: **Mutex) u64;

pub fn lock_mutex(mutex: *Mutex) u64;
pub struct Mutex{
handle: **MutexHandle;
}

pub fn create_mutex(mutex: **MutexHandle) u64;

pub fn lock_mutex(mutex: *MutexHandle) u64;

pub fn new_mutex() Mutex{
let m = Mutex{};
m.handle = &&MutexHandle{};
create_mutex(m.handle);
return m;
}

pub fn unlock_mutex(mutex: *Mutex) u64;
impl Mutex{
pub fn lock() u64{
return lock_mutex(*self.handle);
}

pub fn unlock() u64{
return unlock_mutex(*self.handle);
}
}


pub fn unlock_mutex(mutex: *MutexHandle) u64;

pub fn drop_mutex(mutex: *Mutex) u64;

pub struct CondVar{}

pub fn create_condvar(condvar: **CondVar) u64;

pub fn condvar_wait(condvar: *CondVar, mutex: *Mutex) u64;
pub fn condvar_wait(condvar: *CondVar, mutex: *MutexHandle) u64;

pub fn condvar_notify(condvar: *CondVar) u64;

Expand Down
2 changes: 1 addition & 1 deletion planglib/std/task/delay.pi
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::task::executor;



pub struct DelayTask {
struct DelayTask {
first:bool;
ready:bool;
delay:u64;
Expand Down
63 changes: 63 additions & 0 deletions planglib/std/task/helper.pi
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::task::Task;
use std::task::executor;
use std::task::reactor;
use std::thread;
use std::mutex;


pub fn spawn_async_main<T>(t:Task<T>) void {
Expand All @@ -19,3 +20,65 @@ pub fn spawn_async_main<T>(t:Task<T>) void {
reactor::start_global_reactor();
return;
}


struct AdapterTask<T> {
task:Task<T>;
wk:||=>void;
ready:bool;
mtx:mutex::Mutex;
}

impl<T> Task<T> for AdapterTask<T> {
fn poll(wk:||=>void) Option<T> {
if self.ready {
let r = self.task.poll(|| => {
return;
});
return r;
}
self.mtx.lock();
if !self.ready {
self.wk = ||=>{
self.mtx.unlock();
wk();
return;
};
} else {
self.mtx.unlock();
wk();
let r = self.task.poll(|| => {
return;
});
return r;
}
self.mtx.unlock();
let r = self.task.poll(|| => {
return;
});
return r;
}
}


pub fn spawn<T>(tk:Task<T>) Task<T> {
let t = AdapterTask<T> {
task:tk,
ready:false,
mtx:mutex::new_mutex(),
};
t.wk = ||=>{
t.mtx.unlock();
return;
};
tk.poll(||=>{
t.mtx.lock();
t.ready = true;
t.wk();

return;
});
return t as Task<T>;
}


7 changes: 6 additions & 1 deletion src/ast/compiler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use super::node::program::ModWrapper;
use super::node::program::ASSET_PATH;
#[cfg(feature = "llvm")]
use crate::ast::jit_config::IS_JIT;
use crate::lsp::mem_docs::COMPILE_INPUT_CACHE;
use crate::{
ast::{accumulators::ModBuffer, node::program::Program},
lsp::mem_docs::{FileCompileInput, MemDocsInput},
Expand Down Expand Up @@ -122,8 +123,11 @@ pub fn compile_dry<'db>(db: &'db dyn Db, docs: MemDocsInput) -> Result<ModWrappe
return Err("project config file not found".to_string());
}

COMPILE_INPUT_CACHE.with(|cache| {
cache.borrow_mut().clear();
});
let parser_entry = docs
.finalize_parser_input(db, docs.file(db).clone(), true)
.finalize_parser_input(db, docs.file(db).clone(), true, Default::default())
.unwrap();

log::trace!("entering compile_dry_file");
Expand Down Expand Up @@ -183,6 +187,7 @@ pub fn compile_dry_file<'db>(
parser_entry.docs(db),
parser_entry.config(db),
parser_entry.opt(db),
parser_entry.parent_mods(db),
);
log::trace!("entering emit");
Some(program.emit(db))
Expand Down
1 change: 1 addition & 0 deletions src/ast/diag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ define_diag!(
GENERATOR_FUNCTION_CANNOT_RETURN_VOID = "generator function cannot return void",
MACRO_EXPANSION_FAILED = "macro expansion failed",
SYNTAX_ERROR_FUNC_PARAM = "syntax error: function parameter",
ONLY_AWAIT_IN_ASYNC_FN = "await is only expected in async functions",
);

define_diag! {
Expand Down
16 changes: 8 additions & 8 deletions src/ast/expects/test_diag.pi.expect
Original file line number Diff line number Diff line change
Expand Up @@ -529,12 +529,12 @@
start: Pos {
line: 163,
column: 17,
offset: 1709,
offset: 1704,
},
end: Pos {
line: 163,
column: 18,
offset: 1710,
offset: 1705,
},
},
},
Expand Down Expand Up @@ -901,8 +901,8 @@
},
end: Pos {
line: 1,
column: 13,
offset: 12,
column: 20,
offset: 19,
},
},
},
Expand Down Expand Up @@ -1527,12 +1527,12 @@
start: Pos {
line: 163,
column: 20,
offset: 1712,
offset: 1707,
},
end: Pos {
line: 163,
column: 21,
offset: 1713,
offset: 1708,
},
},
},
Expand All @@ -1542,12 +1542,12 @@
start: Pos {
line: 163,
column: 20,
offset: 1712,
offset: 1707,
},
end: Pos {
line: 163,
column: 21,
offset: 1713,
offset: 1708,
},
},
},
Expand Down
11 changes: 11 additions & 0 deletions src/ast/node/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ impl Node for UnaryOpNode {
.new_output(pltype.clone()),
(_, TokenType::BIT_NOT) => builder.build_bit_not(exp).new_output(pltype.clone()),
(_, TokenType::AWAIT) => {
if !ctx
.generator_data
.as_ref()
.map(|x| x.borrow().generator_type == GeneratorType::Async)
.unwrap_or_default()
{
self.range
.new_err(ErrorCode::ONLY_AWAIT_IN_ASYNC_FN)
.add_label(exp_range, ctx.get_file(), None)
.add_to_ctx(ctx);
}
let poll_ty = match &*pltype.borrow() {
PLType::Trait(st) => {
if !st.name.starts_with("Task") {
Expand Down
1 change: 1 addition & 0 deletions src/ast/node/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ impl Node for UseNode {
///
/// TODO: 区分该节点与ExternTypeName节点,该节点不生成类型,只生成函数与变量/常量
#[node]
#[derive(Default)]
pub struct ExternIdNode {
/// namespace refers to the namespace of an identifier
/// it might be empty
Expand Down
1 change: 1 addition & 0 deletions src/ast/node/primary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ impl Node for NumNode {
}

#[node]
#[derive(Default)]
pub struct VarNode {
/// identifier name of a symbol, which could be either a variable or a type
pub name: Ustr,
Expand Down
30 changes: 26 additions & 4 deletions src/ast/node/program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use crate::ast::pltype::add_primitive_types;
use crate::ast::pltype::FNValue;
use crate::ast::tokens::TokenType;
use crate::flow::display::Dot;
use crate::format_label;
use crate::lsp::semantic_tokens::SemanticTokensBuilder;
use crate::lsp::text;
#[cfg(feature = "repl")]
Expand Down Expand Up @@ -274,8 +275,13 @@ impl<'db> Program<'db> {
} else {
("检查", &CHECK_PROGRESS as &ProgressBar)
};
let parents = self.parent_mods(db);
// parse all dependencies into modules and process symbols into the main module symbol table
for (i, u) in entry_node.uses.iter().enumerate() {
let mut parents = parents.clone();
parents
.0
.insert(self.params(db).file(db).to_string(), u.range());
#[cfg(not(target_arch = "wasm32"))]
pb.set_message(format!(
"正在{}包{}的依赖项{}/{}",
Expand Down Expand Up @@ -312,9 +318,23 @@ impl<'db> Program<'db> {
let mut mod_id = wrapper.use_node(db).get_last_id();

let dep_path_str = dep_path.to_str().unwrap().to_string();
let mut dep_parser_entry =
self.docs(db)
.finalize_parser_input(db, dep_path_str.clone(), false);
if parents.0.contains_key(&dep_path_str) {
let mut diag = u.range().new_err(ErrorCode::CYCLE_DEPENDENCY);
diag.set_source(&dep_path_str);
for (f, r) in parents.0.iter() {
let msg = "import in cycle here";
diag.add_label(*r, ustr(f), format_label!(msg));
}
Diagnostics((dep_path_str.clone(), vec![diag])).accumulate(db);
continue;
}
parents.0.insert(dep_path_str.clone(), u.range());
let mut dep_parser_entry = self.docs(db).finalize_parser_input(
db,
dep_path_str.clone(),
false,
parents.clone(),
);

#[cfg(target_arch = "wasm32")]
if dep_path_str.starts_with("core") || dep_path_str.starts_with("std") {
Expand All @@ -329,7 +349,9 @@ impl<'db> Program<'db> {
if let Some(p) = dep_path.parent() {
mod_id = Some(p.file_name().unwrap().to_str().unwrap().to_string().into());
let file = p.with_extension("pi").to_str().unwrap().to_string();
dep_parser_entry = self.docs(db).finalize_parser_input(db, file, false);
dep_parser_entry = self
.docs(db)
.finalize_parser_input(db, file, false, parents);
symbol_opt = Some(
dep_path
.with_extension("")
Expand Down
Loading

0 comments on commit 4740c6e

Please sign in to comment.