chore(els): let FileCache: Send + Sync

This commit is contained in:
Shunsuke Shibayama 2023-05-01 23:24:47 +09:00
parent 591440333a
commit c84294fc11
15 changed files with 101 additions and 88 deletions

View file

@ -353,9 +353,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
// TODO: multiline comments
if self
.file_cache
.get(&uri)
.unwrap()
.get_line(pos.line)
.get_line(&uri, pos.line)
.map_or(false, |line| line.starts_with('#'))
{
return Ok(None);

View file

@ -65,7 +65,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
let dependents = self.dependents_of(&uri);
for dep in dependents {
// _log!("dep: {dep}");
let code = self.file_cache.get_code(&dep)?.to_string();
let code = self.file_cache.get_entire_code(&dep)?.to_string();
self.check_file(dep, code)?;
}
Ok(())
@ -76,19 +76,19 @@ impl<Checker: BuildRunnable> Server<Checker> {
let Some(ts) = self.file_cache.get_token_stream(&uri) else {
return Ok(());
};
let mut parser = Parser::new(ts.clone());
let mut parser = Parser::new(ts);
if parser.parse().is_err() {
return Ok(());
}
let path = util::uri_to_path(&uri);
let code = &self.file_cache.get(&uri).unwrap().code;
let code = self.file_cache.get_entire_code(&uri)?;
let mode = if path.to_string_lossy().ends_with(".d.er") {
"declare"
} else {
"exec"
};
let mut checker = self.get_checker(path);
match checker.build(code.into(), mode) {
match checker.build(code, mode) {
Ok(artifact) => {
self.artifacts.insert(uri.clone(), artifact.into());
}

View file

@ -10,7 +10,7 @@ use lsp_types::{
};
use erg_common::dict::Dict;
use erg_common::shared::Shared;
use erg_common::shared::AtomicShared;
use erg_common::traits::DequeStream;
use erg_compiler::erg_parser::lex::Lexer;
use erg_compiler::erg_parser::token::{Token, TokenStream};
@ -37,9 +37,9 @@ pub struct FileCacheEntry {
impl FileCacheEntry {
/// line: 0-based
pub fn get_line(&self, line: u32) -> Option<&str> {
pub fn get_line(&self, line: u32) -> Option<String> {
let mut lines = self.code.lines();
lines.nth(line as usize)
lines.nth(line as usize).map(|s| s.to_string())
}
}
@ -47,16 +47,25 @@ impl FileCacheEntry {
/// This struct can save changes in real-time & incrementally.
#[derive(Debug, Clone)]
pub struct FileCache {
pub files: Shared<Dict<NormalizedUrl, FileCacheEntry>>,
pub files: AtomicShared<Dict<NormalizedUrl, FileCacheEntry>>,
}
impl FileCache {
pub fn new() -> Self {
Self {
files: Shared::new(Dict::new()),
files: AtomicShared::new(Dict::new()),
}
}
fn load_once(&self, uri: &NormalizedUrl) -> ELSResult<()> {
if self.files.borrow_mut().get(uri).is_some() {
return Ok(());
}
let code = _get_code_from_uri(uri)?;
self.update(uri, code, None);
Ok(())
}
pub(crate) fn set_capabilities(&mut self, capabilities: &mut ServerCapabilities) {
let workspace_folders = WorkspaceFoldersServerCapabilities {
supported: Some(true),
@ -98,36 +107,28 @@ impl FileCache {
capabilities.text_document_sync = Some(TextDocumentSyncCapability::Options(sync_option));
}
pub fn get_code(&self, uri: &NormalizedUrl) -> ELSResult<&str> {
Ok(self.get(uri)?.code.as_str())
/// This method clones and returns the entire file.
/// If you only need part of the file, use `get_ranged` or `get_line` instead.
pub fn get_entire_code(&self, uri: &NormalizedUrl) -> ELSResult<String> {
self.load_once(uri)?;
Ok(self
.files
.borrow_mut()
.get(uri)
.ok_or("not found")?
.code
.clone())
}
pub fn get(&self, uri: &NormalizedUrl) -> ELSResult<&FileCacheEntry> {
let Some(entry) = unsafe { self.files.as_ref() }.get(uri) else {
let code = _get_code_from_uri(uri)?;
self.update(uri, code, None);
let entry = unsafe { self.files.as_ref() }.get(uri).ok_or("not found")?;
return Ok(entry);
};
Ok(entry)
}
pub fn get_token_stream(&self, uri: &NormalizedUrl) -> Option<&TokenStream> {
self.get(uri).ok().and_then(|ent| ent.token_stream.as_ref())
}
pub fn get_token_index(&self, uri: &NormalizedUrl, pos: Position) -> Option<usize> {
let tokens = self.get_token_stream(uri)?;
for (i, tok) in tokens.iter().enumerate() {
if util::pos_in_loc(tok, pos) {
return Some(i);
}
}
None
pub fn get_token_stream(&self, uri: &NormalizedUrl) -> Option<TokenStream> {
let _ = self.load_once(uri);
self.files.borrow_mut().get(uri)?.token_stream.clone()
}
pub fn get_token(&self, uri: &NormalizedUrl, pos: Position) -> Option<Token> {
let tokens = self.get_token_stream(uri)?;
let _ = self.load_once(uri);
let ent = self.files.borrow_mut();
let tokens = ent.get(uri)?.token_stream.as_ref()?;
for tok in tokens.iter() {
if util::pos_in_loc(tok, pos) {
return Some(tok.clone());
@ -142,8 +143,17 @@ impl FileCache {
pos: Position,
offset: isize,
) -> Option<Token> {
let tokens = self.get_token_stream(uri)?;
let index = self.get_token_index(uri, pos)?;
let _ = self.load_once(uri);
let ent = self.files.borrow_mut();
let tokens = ent.get(uri)?.token_stream.as_ref()?;
let index = (|| {
for (i, tok) in tokens.iter().enumerate() {
if util::pos_in_loc(tok, pos) {
return Some(i);
}
}
None
})()?;
let index = (index as isize + offset) as usize;
if index < tokens.len() {
Some(tokens[index].clone())
@ -153,8 +163,9 @@ impl FileCache {
}
/// 0-based
pub(crate) fn get_line(&self, uri: &NormalizedUrl, line: u32) -> Option<&str> {
self.get(uri).ok().and_then(|ent| ent.get_line(line))
pub(crate) fn get_line(&self, uri: &NormalizedUrl, line: u32) -> Option<String> {
let _ = self.load_once(uri);
self.files.borrow_mut().get(uri)?.get_line(line)
}
pub(crate) fn get_ranged(
@ -162,7 +173,9 @@ impl FileCache {
uri: &NormalizedUrl,
range: Range,
) -> ELSResult<Option<String>> {
let file = self.get(uri)?;
self.load_once(uri)?;
let ent = self.files.borrow_mut();
let file = ent.get(uri).ok_or("not found")?;
let mut code = String::new();
for (i, line) in file.code.lines().enumerate() {
if i >= range.start.line as usize && i <= range.end.line as usize {
@ -191,7 +204,8 @@ impl FileCache {
}
pub(crate) fn update(&self, uri: &NormalizedUrl, code: String, ver: Option<i32>) {
let entry = unsafe { self.files.as_ref() }.get(uri);
let ent = self.files.borrow_mut();
let entry = ent.get(uri);
if let Some(entry) = entry {
if ver.map_or(false, |ver| ver <= entry.ver) {
// crate::_log!("171: double update detected: {ver:?}, {}, code:\n{}", entry.ver, entry.code);
@ -206,6 +220,7 @@ impl FileCache {
1
}
});
drop(ent);
self.files.borrow_mut().insert(
uri.clone(),
FileCacheEntry {
@ -217,7 +232,8 @@ impl FileCache {
}
pub(crate) fn ranged_update(&self, uri: &NormalizedUrl, old: Range, new_code: &str) {
let Some(entry) = unsafe { self.files.as_mut() }.get_mut(uri) else {
let mut ent = self.files.borrow_mut();
let Some(entry) = ent.get_mut(uri) else {
return;
};
let mut code = entry.code.clone();
@ -232,7 +248,8 @@ impl FileCache {
pub(crate) fn incremental_update(&self, params: DidChangeTextDocumentParams) {
let uri = NormalizedUrl::new(params.text_document.uri);
let Some(entry) = unsafe { self.files.as_mut() }.get_mut(&uri) else {
let mut ent = self.files.borrow_mut();
let Some(entry) = ent.get_mut(&uri) else {
return;
};
if entry.ver >= params.text_document.version {

View file

@ -230,7 +230,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
};
let mut prev_token = Token::DUMMY;
loop {
let Some(token) = util::get_token_from_stream(stream, def_pos)? else {
let Some(token) = util::get_token_from_stream(&stream, def_pos)? else {
next!(def_pos, default_code_block, contents);
};
if token.deep_eq(&prev_token) {

View file

@ -91,7 +91,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
}
// recheck dependencies and finally the file itself
for dep in dependencies {
let code = self.file_cache.get_code(&dep)?.to_string();
let code = self.file_cache.get_entire_code(&dep)?.to_string();
self.check_file(dep, code)?;
}
// dependents are checked after changes are committed

View file

@ -290,9 +290,9 @@ impl<Checker: BuildRunnable> Server<Checker> {
send_log(format!("full semantic tokens request: {params:?}"))?;
let uri = NormalizedUrl::new(params.text_document.uri);
let path = util::uri_to_path(&uri);
let src = self.file_cache.get_code(&uri)?;
let src = self.file_cache.get_entire_code(&uri)?;
let mut builder = ASTBuilder::new(self.cfg.inherit(path));
let result = match builder.build_without_desugaring(src.to_string()) {
let result = match builder.build_without_desugaring(src) {
Ok(ast) => {
let mut state = ASTSemanticState::new();
let tokens = state.enumerate_tokens(ast);

View file

@ -507,7 +507,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
let uri =
NormalizedUrl::parse(msg["params"]["textDocument"]["uri"].as_str().unwrap())?;
send_log(format!("{method}: {uri}"))?;
let code = self.file_cache.get_code(&uri)?.to_string();
let code = self.file_cache.get_entire_code(&uri)?;
self.clear_cache(&uri);
self.check_file(uri, code)
}

View file

@ -86,11 +86,9 @@ impl<Checker: BuildRunnable> Server<Checker> {
args_loc: erg_common::error::Location,
token: &Token,
) -> usize {
let tks = self.file_cache.get_token_stream(uri).unwrap();
// we should use the latest commas
let commas = self
.file_cache
.get_token_stream(uri)
.unwrap()
let commas = tks
.iter()
.skip_while(|&tk| tk.loc() < args_loc)
.filter(|tk| tk.is(TokenKind::Comma) && args_loc.ln_end() >= tk.ln_begin())

View file

@ -1,15 +1,15 @@
use std::borrow::{Borrow, ToOwned};
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::Rc;
use std::sync::Arc;
use std::thread::LocalKey;
use crate::dict::Dict;
use crate::set::Set;
use crate::{RcArray, Str};
use crate::{ArcArray, Str};
#[derive(Debug)]
pub struct CacheSet<T: ?Sized>(RefCell<Set<Rc<T>>>);
pub struct CacheSet<T: ?Sized>(RefCell<Set<Arc<T>>>);
impl<T: ?Sized> Default for CacheSet<T> {
fn default() -> Self {
@ -47,31 +47,31 @@ impl CacheSet<str> {
}
impl<T: Hash + Eq + Clone> CacheSet<[T]> {
pub fn get(&self, q: &[T]) -> Rc<[T]> {
pub fn get(&self, q: &[T]) -> Arc<[T]> {
if let Some(cached) = self.0.borrow().get(q) {
return cached.clone();
} // &self.0 is dropped
let s = RcArray::from(q);
let s = ArcArray::from(q);
self.0.borrow_mut().insert(s.clone());
s
}
}
impl<T: Hash + Eq> CacheSet<T> {
pub fn get<Q: ?Sized + Hash + Eq>(&self, q: &Q) -> Rc<T>
pub fn get<Q: ?Sized + Hash + Eq>(&self, q: &Q) -> Arc<T>
where
Rc<T>: Borrow<Q>,
Arc<T>: Borrow<Q>,
Q: ToOwned<Owned = T>,
{
if let Some(cached) = self.0.borrow().get(q) {
return cached.clone();
} // &self.0 is dropped
let s = Rc::from(q.to_owned());
let s = Arc::from(q.to_owned());
self.0.borrow_mut().insert(s.clone());
s
}
}
pub struct CacheDict<K, V: ?Sized>(RefCell<Dict<K, Rc<V>>>);
pub struct CacheDict<K, V: ?Sized>(RefCell<Dict<K, Arc<V>>>);
pub struct GlobalCacheDict<K: 'static, V: ?Sized + 'static>(LocalKey<RefCell<CacheDict<K, V>>>);

View file

@ -39,7 +39,7 @@ use crate::set::Set;
pub use crate::str::Str;
pub use crate::triple::Triple;
pub type RcArray<T> = std::rc::Rc<[T]>;
pub type ArcArray<T> = std::sync::Arc<[T]>;
pub fn open_read(filename: &str) -> std::io::Result<String> {
let f = std::fs::File::open(filename)?;

View file

@ -3,14 +3,14 @@ use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::{Add, Deref};
pub type RcStr = std::rc::Rc<str>;
pub type ArcStr = std::sync::Arc<str>;
/// Used to hold an immutable string.
///
/// It can construct as a const (by Str::ever).
#[derive(Debug, Clone, Eq)]
pub enum Str {
Rc(RcStr),
Rc(ArcStr),
Static(&'static str),
}
@ -93,16 +93,16 @@ impl From<String> for Str {
}
}
impl From<&RcStr> for Str {
impl From<&ArcStr> for Str {
#[inline]
fn from(s: &RcStr) -> Self {
fn from(s: &ArcStr) -> Self {
Str::Rc(s.clone())
}
}
impl From<RcStr> for Str {
impl From<ArcStr> for Str {
#[inline]
fn from(s: RcStr) -> Self {
fn from(s: ArcStr) -> Self {
Str::Rc(s)
}
}
@ -159,10 +159,10 @@ impl Str {
Str::Rc(s.into())
}
pub fn into_rc(self) -> RcStr {
pub fn into_rc(self) -> ArcStr {
match self {
Str::Rc(s) => s,
Str::Static(s) => RcStr::from(s),
Str::Static(s) => ArcStr::from(s),
}
}

View file

@ -7,7 +7,7 @@ use erg_common::log;
use erg_common::set::Set;
use erg_common::traits::{Locational, Stream};
use erg_common::{dict, fmt_vec, fn_name, option_enum_unwrap, set};
use erg_common::{RcArray, Str};
use erg_common::{ArcArray, Str};
use OpKind::*;
use erg_parser::ast::Dict as AstDict;
@ -374,7 +374,7 @@ impl Context {
let elem = self.eval_const_expr(&elem.expr)?;
elems.push(elem);
}
Ok(ValueObj::Array(RcArray::from(elems)))
Ok(ValueObj::Array(ArcArray::from(elems)))
}
_ => Err(EvalErrors::from(EvalError::not_const_expr(
self.cfg.input.clone(),
@ -434,7 +434,7 @@ impl Context {
}
}
}
Ok(ValueObj::Tuple(RcArray::from(elems)))
Ok(ValueObj::Tuple(ArcArray::from(elems)))
}
fn eval_const_record(&self, record: &Record) -> EvalResult<ValueObj> {

View file

@ -9,7 +9,7 @@ use erg_common::python_util::PythonVersion;
use erg_common::serialize::DataTypePrefix;
use erg_common::traits::ExitStatus;
use erg_common::{fn_name, switch_lang};
use erg_common::{RcArray, Str};
use erg_common::{ArcArray, Str};
use super::codeobj::CodeObj;
use super::constructors::array_t;
@ -282,7 +282,7 @@ impl Deserializer {
&mut self,
v: &mut Vec<u8>,
python_ver: PythonVersion,
) -> DeserializeResult<RcArray<ValueObj>> {
) -> DeserializeResult<ArcArray<ValueObj>> {
match self.deserialize_const(v, python_ver)? {
ValueObj::Array(arr) => Ok(arr),
other => Err(DeserializeError::type_error(&Type::Str, other.ref_t())),

View file

@ -1,7 +1,7 @@
use std::cmp::Ordering;
use std::fmt;
use std::ops::{Add, Div, Mul, Neg, Range, RangeInclusive, Sub};
use std::rc::Rc;
use std::sync::Arc;
use erg_common::dict::Dict;
use erg_common::set;
@ -522,14 +522,14 @@ impl TryFrom<TyParam> for ValueObj {
for tp in tps {
vals.push(ValueObj::try_from(tp)?);
}
Ok(ValueObj::Array(Rc::from(vals)))
Ok(ValueObj::Array(Arc::from(vals)))
}
TyParam::Tuple(tps) => {
let mut vals = vec![];
for tp in tps {
vals.push(ValueObj::try_from(tp)?);
}
Ok(ValueObj::Tuple(Rc::from(vals)))
Ok(ValueObj::Tuple(Arc::from(vals)))
}
TyParam::Dict(tps) => {
let mut vals = dict! {};

View file

@ -5,7 +5,7 @@ use std::cmp::Ordering;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Neg;
use std::rc::Rc;
use std::sync::Arc;
use erg_common::config::Input;
use erg_common::dict::Dict;
@ -15,7 +15,7 @@ use erg_common::python_util::PythonVersion;
use erg_common::serialize::*;
use erg_common::set::Set;
use erg_common::{dict, fmt_iter, impl_display_from_debug, log, switch_lang};
use erg_common::{RcArray, Str};
use erg_common::{ArcArray, Str};
use erg_parser::ast::{ConstArgs, ConstExpr};
use crate::context::eval::type_from_token_kind;
@ -436,10 +436,10 @@ pub enum ValueObj {
Float(f64),
Str(Str),
Bool(bool),
Array(Rc<[ValueObj]>),
Array(ArcArray<ValueObj>),
Set(Set<ValueObj>),
Dict(Dict<ValueObj, ValueObj>),
Tuple(Rc<[ValueObj]>),
Tuple(ArcArray<ValueObj>),
Record(Dict<Field, ValueObj>),
DataClass {
name: Str,
@ -659,7 +659,7 @@ impl From<CodeObj> for ValueObj {
impl<V: Into<ValueObj>> From<Vec<V>> for ValueObj {
fn from(item: Vec<V>) -> Self {
ValueObj::Array(RcArray::from(
ValueObj::Array(ArcArray::from(
&item.into_iter().map(Into::into).collect::<Vec<_>>()[..],
))
}
@ -667,7 +667,7 @@ impl<V: Into<ValueObj>> From<Vec<V>> for ValueObj {
impl<const N: usize, V: Into<ValueObj>> From<[V; N]> for ValueObj {
fn from(item: [V; N]) -> Self {
ValueObj::Array(RcArray::from(&item.map(Into::into)[..]))
ValueObj::Array(ArcArray::from(&item.map(Into::into)[..]))
}
}
@ -912,7 +912,7 @@ impl ValueObj {
}
pub fn tuple_from_const_args(args: ConstArgs) -> Self {
Self::Tuple(Rc::from(&Self::vec_from_const_args(args)[..]))
Self::Tuple(Arc::from(&Self::vec_from_const_args(args)[..]))
}
pub fn vec_from_const_args(args: ConstArgs) -> Vec<Self> {
@ -1012,7 +1012,7 @@ impl ValueObj {
(Self::Float(l), Self::Int(r)) => Some(Self::Float(l - r as f64)),
(Self::Str(l), Self::Str(r)) => Some(Self::Str(Str::from(format!("{l}{r}")))),
(Self::Array(l), Self::Array(r)) => {
let arr = Rc::from([l, r].concat());
let arr = Arc::from([l, r].concat());
Some(Self::Array(arr))
}
(Self::Dict(l), Self::Dict(r)) => Some(Self::Dict(l.concat(r))),