forked from katzen-cafe/iowo
lang: remove this attempt
This commit is contained in:
parent
1c6180aabc
commit
eb7806572b
19 changed files with 1 additions and 1885 deletions
|
@ -1,17 +0,0 @@
|
|||
use std::path::PathBuf;
|
||||
|
||||
use crate::lst_parser::error::SyntaxError;
|
||||
|
||||
use super::files::FileId;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Syntax(FileId, SyntaxError),
|
||||
FailedToOpenFileMod(PathBuf, std::io::Error),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
pub fn from_syntax(file: FileId, e: SyntaxError) -> Self {
|
||||
Self::Syntax(file, e)
|
||||
}
|
||||
}
|
|
@ -1,131 +0,0 @@
|
|||
use std::{
|
||||
clone, fs,
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
|
||||
use dashmap::DashMap;
|
||||
use rowan::ast::{AstNode, AstPtr};
|
||||
|
||||
use crate::{
|
||||
lst_parser::{
|
||||
error::SyntaxError, grammar::source_file, input::Input, output::Output, syntax_kind, Parser,
|
||||
},
|
||||
Lang,
|
||||
};
|
||||
|
||||
use super::{error::Error, modules::Module, nodes};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Loc<N: AstNode<Language = Lang>> {
|
||||
file: FileId,
|
||||
syntax: AstPtr<N>,
|
||||
}
|
||||
|
||||
impl<N: AstNode<Language = Lang>> Loc<N> {
|
||||
pub fn new(node: N, file: FileId) -> Self {
|
||||
Self {
|
||||
file,
|
||||
syntax: AstPtr::new(&node),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn file(&self) -> FileId {
|
||||
self.file
|
||||
}
|
||||
|
||||
pub fn syntax(&self) -> AstPtr<N> {
|
||||
self.syntax.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// global file store
|
||||
/// contains all known files etc.
|
||||
#[derive(Clone)]
|
||||
pub struct Files {
|
||||
paths: Arc<Mutex<Vec<PathBuf>>>,
|
||||
store: Arc<DashMap<PathBuf, Arc<SourceFile>>>,
|
||||
}
|
||||
|
||||
impl Files {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
paths: Arc::new(Mutex::new(Vec::new())),
|
||||
store: Arc::new(DashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_and_parse(&self, path: &Path) -> Result<(FileId, Vec<Error>), std::io::Error> {
|
||||
let (file, errors) = SourceFile::read_and_parse(&path)?;
|
||||
|
||||
// add file to paths and unlock again
|
||||
let id = {
|
||||
let path = path.clone();
|
||||
let mut paths = self.paths.lock().unwrap();
|
||||
let r = paths.len();
|
||||
paths.push(path.to_path_buf());
|
||||
FileId(r)
|
||||
};
|
||||
|
||||
let _ = self.store.insert(path.to_path_buf(), Arc::new(file));
|
||||
|
||||
Ok((
|
||||
id,
|
||||
errors
|
||||
.into_iter()
|
||||
.map(|e| Error::from_syntax(id, e))
|
||||
.collect(),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get(&self, id: FileId) -> Arc<SourceFile> {
|
||||
let path = {
|
||||
let paths = self.paths.lock().unwrap();
|
||||
paths[id.0].clone()
|
||||
};
|
||||
|
||||
self.store.get(&path).unwrap().clone()
|
||||
}
|
||||
|
||||
pub fn get_path(&self, id: FileId) -> PathBuf {
|
||||
let paths = self.paths.lock().unwrap();
|
||||
paths[id.0].clone()
|
||||
}
|
||||
|
||||
pub fn path_store(&self) -> Arc<Mutex<Vec<PathBuf>>> {
|
||||
self.paths.clone()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SourceFile {
|
||||
pub lst: RwLock<rowan::GreenNode>,
|
||||
root_module: Option<Arc<Module>>,
|
||||
}
|
||||
|
||||
impl SourceFile {
|
||||
fn read_and_parse(path: &Path) -> Result<(Self, Vec<SyntaxError>), std::io::Error> {
|
||||
let source_text = fs::read_to_string(&path)?;
|
||||
|
||||
let toks = syntax_kind::lex(&source_text);
|
||||
let input = Input::new(&toks);
|
||||
let mut parser = Parser::new(input);
|
||||
|
||||
source_file(&mut parser);
|
||||
|
||||
let events = parser.finish();
|
||||
let out = Output::from_parser_output(toks, events);
|
||||
// let lst = out.syntax();
|
||||
let (lst, errors) = out.dissolve();
|
||||
|
||||
Ok((
|
||||
Self {
|
||||
lst: RwLock::new(lst),
|
||||
root_module: None,
|
||||
},
|
||||
errors,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct FileId(pub usize);
|
|
@ -1,175 +0,0 @@
|
|||
use std::{
|
||||
string::String,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use dashmap::{DashMap, DashSet};
|
||||
use rowan::ast::{AstNode, AstPtr};
|
||||
|
||||
use crate::SyntaxNode;
|
||||
|
||||
use super::{
|
||||
files::{FileId, Loc},
|
||||
nodes,
|
||||
registry::ItemPath,
|
||||
world_creation_pool::{wait_blocker::WaitBlocker, Task, WorkerCtx},
|
||||
};
|
||||
|
||||
pub struct Module {
|
||||
/// is none if this is the root module of the entry point file
|
||||
decl: Option<Loc<nodes::Mod>>,
|
||||
body: ModuleBody,
|
||||
own_path: ItemPath,
|
||||
child_modules: Arc<DashMap<String, Arc<Module>>>,
|
||||
child_defs: Arc<DashMap<String, Def>>,
|
||||
}
|
||||
|
||||
impl Module {
|
||||
pub fn parse_mod_body(
|
||||
ctx: &WorkerCtx,
|
||||
tree: SyntaxNode,
|
||||
path: ItemPath,
|
||||
file: FileId,
|
||||
) -> (DashMap<String, Arc<Mutex<Option<Arc<Module>>>>>, Vec<Task>) {
|
||||
let children: Vec<(String, Arc<Mutex<Option<Arc<Module>>>>, nodes::Mod)> = tree
|
||||
.children()
|
||||
.filter_map(|c| nodes::Mod::cast(c))
|
||||
.map(|m| {
|
||||
let name = nodes::ModName::cast(m.syntax().first_child().unwrap())
|
||||
.expect("ModName should always be first child of Mod");
|
||||
(
|
||||
name.syntax().text().to_string(),
|
||||
Arc::new(Mutex::new(None)),
|
||||
m,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
let retv: DashMap<String, Arc<Mutex<Option<Arc<Module>>>>> = children
|
||||
.iter()
|
||||
.map(|(name, mod_r, _)| (name.to_owned(), mod_r.clone()))
|
||||
.collect();
|
||||
let tasks = children
|
||||
.into_iter()
|
||||
.map(|(name, mod_r, loc)| {
|
||||
let mut path = path.clone();
|
||||
path.push(name);
|
||||
Task::ParseMod(Loc::new(loc, file), mod_r, path)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
(retv, tasks)
|
||||
}
|
||||
pub fn parse_file0(
|
||||
ctx: &mut WorkerCtx,
|
||||
file: FileId,
|
||||
path: ItemPath,
|
||||
decl: Option<Loc<nodes::Mod>>,
|
||||
) {
|
||||
let tree = ctx.get_tree(file);
|
||||
|
||||
let (retv, tasks) = Self::parse_mod_body(ctx, tree, path.clone(), file);
|
||||
|
||||
ctx.send_tasks(
|
||||
Task::ParseFileMod1 {
|
||||
file,
|
||||
decl,
|
||||
ret: retv.into(),
|
||||
path,
|
||||
},
|
||||
tasks,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_mod(
|
||||
ctx: &mut WorkerCtx,
|
||||
loc: Loc<nodes::Mod>,
|
||||
path: ItemPath,
|
||||
ret: Arc<Mutex<Option<Module>>>,
|
||||
blocker: WaitBlocker,
|
||||
) {
|
||||
let mod_decl = ctx.resolve_loc(loc);
|
||||
let children = mod_decl.syntax().children().collect::<Vec<_>>();
|
||||
|
||||
if children.len() == 1 {
|
||||
// TODO: file mod
|
||||
todo!()
|
||||
} else if children.len() == 2 {
|
||||
// inline mod
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_inline_mod(
|
||||
ctx: &mut WorkerCtx,
|
||||
decl: Loc<nodes::Mod>,
|
||||
path: ItemPath,
|
||||
ret: Arc<Mutex<Option<Arc<Module>>>>,
|
||||
blocker: WaitBlocker,
|
||||
) {
|
||||
let mod_decl_children = ctx
|
||||
.resolve_loc(decl.clone())
|
||||
.syntax()
|
||||
.children()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
assert_eq!(mod_decl_children.len(), 2);
|
||||
|
||||
let name = nodes::ModName::cast(mod_decl_children[0].clone()).unwrap();
|
||||
let body = nodes::ModBody::cast(mod_decl_children[1].clone()).unwrap();
|
||||
|
||||
let (retv, tasks) =
|
||||
Self::parse_mod_body(ctx, body.syntax().clone(), path.clone(), decl.file());
|
||||
|
||||
ctx.send_tasks(
|
||||
Task::CompleteMod {
|
||||
ret_self: ret,
|
||||
body: ModuleBody::Inline(Loc::new(body, decl.file())),
|
||||
decl: Some(decl),
|
||||
path,
|
||||
child_mods: retv.into(),
|
||||
blocker,
|
||||
},
|
||||
tasks,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn complete_mod(
|
||||
ctx: &mut WorkerCtx,
|
||||
decl: Option<Loc<nodes::Mod>>,
|
||||
ret_self: Arc<Mutex<Option<Arc<Module>>>>,
|
||||
body: ModuleBody,
|
||||
path: ItemPath,
|
||||
child_mods: Arc<DashMap<String, Arc<Mutex<Option<Arc<Module>>>>>>,
|
||||
blocker: WaitBlocker,
|
||||
) {
|
||||
assert!(child_mods
|
||||
.iter()
|
||||
.all(|item| item.value().lock().unwrap().is_some()));
|
||||
|
||||
let module = Arc::new(Module {
|
||||
decl,
|
||||
body,
|
||||
own_path: path,
|
||||
child_modules: Arc::new(
|
||||
Arc::<_>::unwrap_or_clone(child_mods)
|
||||
.into_iter()
|
||||
.map(|(name, module)| {
|
||||
let module = module.lock().unwrap().take().unwrap();
|
||||
(name, module)
|
||||
})
|
||||
.collect(),
|
||||
),
|
||||
child_defs: Arc::default(),
|
||||
});
|
||||
|
||||
let mut ret_self = ret_self.lock().unwrap();
|
||||
*ret_self = Some(module);
|
||||
drop(blocker);
|
||||
}
|
||||
}
|
||||
|
||||
struct Def;
|
||||
|
||||
pub enum ModuleBody {
|
||||
Inline(Loc<nodes::ModBody>),
|
||||
File(FileId),
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
use crate::lst_parser::syntax_kind::SyntaxKind::*;
|
||||
use crate::SyntaxNode;
|
||||
use rowan::Language;
|
||||
macro_rules! ast_nodes {
|
||||
($($ast:ident, $kind:ident);+) => {
|
||||
$(
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
pub struct $ast(SyntaxNode);
|
||||
impl rowan::ast::AstNode for $ast {
|
||||
type Language = crate::Lang;
|
||||
|
||||
fn can_cast(kind: <Self::Language as Language>::Kind) -> bool {
|
||||
kind == $kind
|
||||
}
|
||||
|
||||
fn cast(node: SyntaxNode) -> Option<Self> {
|
||||
if node.kind() == $kind {
|
||||
Some(Self(node))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn syntax(&self) -> &SyntaxNode {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
ast_nodes!(
|
||||
Def, DEF;
|
||||
DefName, DEF_NAME;
|
||||
DefBody, DEF_BODY;
|
||||
|
||||
Mod, MODULE;
|
||||
ModName, MODULE_NAME;
|
||||
ModBody, MODULE_BODY;
|
||||
|
||||
Use, USE;
|
||||
UsePat, USE_PAT;
|
||||
PatItem, PAT_ITEM;
|
||||
PatGlob, PAT_GLOB;
|
||||
PatGroup, PAT_GROUP;
|
||||
|
||||
Literal, LITERAL;
|
||||
IntLit, INT_NUM;
|
||||
FloatLit, FLOAT_NUM;
|
||||
StringLit, STRING;
|
||||
|
||||
Matrix, MATRIX;
|
||||
MatrixRow, MAT_ROW;
|
||||
Vector, VEC;
|
||||
List, LIST;
|
||||
CollectionItem, COLLECTION_ITEM;
|
||||
|
||||
ParenthesizedExpr, PARENTHESIZED_EXPR;
|
||||
Expression, EXPR;
|
||||
|
||||
Pipeline, PIPELINE;
|
||||
|
||||
Instruction, INSTR;
|
||||
InstructionName, INSTR_NAME;
|
||||
InstructionParams, INSTR_PARAMS;
|
||||
|
||||
AttributeSet, ATTR_SET;
|
||||
Attribute, ATTR;
|
||||
AttributeName, ATTR_NAME;
|
||||
AttributeValue, ATTR_VALUE;
|
||||
|
||||
ParseError, PARSE_ERR;
|
||||
LexError, LEX_ERR;
|
||||
|
||||
Root, ROOT;
|
||||
Eof, EOF
|
||||
);
|
|
@ -1,41 +0,0 @@
|
|||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use dashmap::{DashMap, DashSet};
|
||||
|
||||
use super::modules::Module;
|
||||
|
||||
pub type ItemPath = Vec<String>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Registry(Arc<RegistryInner>);
|
||||
|
||||
impl Registry {
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(RegistryInner::new()))
|
||||
}
|
||||
}
|
||||
|
||||
struct RegistryInner {
|
||||
/// ids will refer to paths of modules
|
||||
paths: RwLock<Vec<ItemPath>>,
|
||||
modules: DashMap<ItemPath, Arc<Module>>,
|
||||
names: DashMap<String, DashSet<RegistryId>>,
|
||||
}
|
||||
|
||||
impl RegistryInner {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
paths: RwLock::new(Vec::new()),
|
||||
modules: DashMap::new(),
|
||||
names: DashMap::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum RegistryId {
|
||||
/// refer to the modlue item itself, the name must be the same as the last item of the path
|
||||
Module(usize),
|
||||
/// refer to a def in a module
|
||||
/// name must refer to name of child of module
|
||||
Def(usize),
|
||||
}
|
|
@ -1,215 +0,0 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{Arc, Mutex},
|
||||
thread,
|
||||
};
|
||||
|
||||
use crossbeam::channel::{Receiver, Sender};
|
||||
use dashmap::DashMap;
|
||||
use rowan::ast::{AstNode, AstPtr};
|
||||
|
||||
use crate::{Lang, SyntaxNode};
|
||||
|
||||
use self::wait_blocker::WaitBlocker;
|
||||
|
||||
use super::{
|
||||
error::Error,
|
||||
files::{FileId, Files, Loc},
|
||||
modules::{Module, ModuleBody},
|
||||
nodes,
|
||||
registry::{ItemPath, Registry},
|
||||
};
|
||||
|
||||
pub mod wait_blocker;
|
||||
|
||||
pub(super) struct WorldCreationPool {
|
||||
workers: Vec<Worker>,
|
||||
tx: Sender<Job>,
|
||||
}
|
||||
|
||||
impl WorldCreationPool {
|
||||
pub fn new(files: Files, reg: Registry) -> Self {
|
||||
let mut workers = Vec::new();
|
||||
let (tx, rx) = crossbeam::channel::unbounded();
|
||||
|
||||
for i in 0..num_cpus::get() {
|
||||
workers.push(Worker::new(
|
||||
i,
|
||||
files.clone(),
|
||||
reg.clone(),
|
||||
tx.clone(),
|
||||
rx.clone(),
|
||||
))
|
||||
}
|
||||
|
||||
Self { workers, tx }
|
||||
}
|
||||
}
|
||||
|
||||
enum Job {
|
||||
WaitingFor(WaitBlocker, Task),
|
||||
Awaited(WaitBlocker, Task),
|
||||
}
|
||||
|
||||
pub enum Task {
|
||||
ParseMod(Loc<nodes::Mod>, Arc<Mutex<Option<Arc<Module>>>>, ItemPath),
|
||||
ParseFileMod0 {
|
||||
file: FileId,
|
||||
decl: Option<Loc<nodes::Mod>>,
|
||||
path: ItemPath,
|
||||
},
|
||||
ParseFileMod1 {
|
||||
file: FileId,
|
||||
decl: Option<Loc<nodes::Mod>>,
|
||||
path: ItemPath,
|
||||
ret: Arc<DashMap<String, Arc<Mutex<Option<Arc<Module>>>>>>,
|
||||
},
|
||||
CompleteMod {
|
||||
ret_self: Arc<Mutex<Option<Arc<Module>>>>,
|
||||
decl: Option<Loc<nodes::Mod>>,
|
||||
body: ModuleBody,
|
||||
path: ItemPath,
|
||||
child_mods: Arc<DashMap<String, Arc<Mutex<Option<Arc<Module>>>>>>,
|
||||
blocker: WaitBlocker,
|
||||
},
|
||||
OpenFile(PathBuf),
|
||||
}
|
||||
|
||||
struct Worker {
|
||||
id: usize,
|
||||
// thead: thread::JoinHandle<Vec<Error>>,
|
||||
thread: thread::JoinHandle<()>,
|
||||
}
|
||||
|
||||
pub struct WorkerCtx {
|
||||
errors: Vec<Error>,
|
||||
files: Files,
|
||||
local_files: HashMap<PathBuf, SyntaxNode>,
|
||||
pub reg: Registry,
|
||||
tx: Sender<Job>,
|
||||
}
|
||||
|
||||
impl WorkerCtx {
|
||||
pub fn get_tree(&mut self, id: FileId) -> SyntaxNode {
|
||||
let p = self.files.get_path(id);
|
||||
if self.local_files.contains_key(&p) {
|
||||
self.local_files[&p].clone()
|
||||
} else {
|
||||
let f = self.files.get(id);
|
||||
let lst = SyntaxNode::new_root(f.lst.read().unwrap().clone());
|
||||
self.local_files.insert(p, lst.clone());
|
||||
lst
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_loc<N: AstNode<Language = Lang>>(&mut self, loc: Loc<N>) -> N {
|
||||
let f = self.get_tree(loc.file());
|
||||
|
||||
loc.syntax().to_node(&f)
|
||||
}
|
||||
|
||||
pub fn send_tasks(&self, task: Task, dependencies: Vec<Task>) {
|
||||
let blocker = WaitBlocker::new();
|
||||
for dep_task in dependencies {
|
||||
self.tx
|
||||
.send(Job::Awaited(blocker.clone(), dep_task))
|
||||
.unwrap();
|
||||
}
|
||||
self.tx.send(Job::WaitingFor(blocker, task)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl Worker {
|
||||
fn new(id: usize, files: Files, reg: Registry, sender: Sender<Job>, rx: Receiver<Job>) -> Self {
|
||||
let thread_handle = thread::spawn(move || {
|
||||
let ctx = WorkerCtx {
|
||||
errors: Vec::new(),
|
||||
local_files: HashMap::new(),
|
||||
files,
|
||||
reg,
|
||||
tx: sender,
|
||||
};
|
||||
|
||||
for job in &rx {
|
||||
// if matches!(job, Job::WithCond(_, _)) {
|
||||
|
||||
// }
|
||||
match job {
|
||||
Job::WaitingFor(blocker, task) => {
|
||||
if blocker.is_ready() {
|
||||
Self::do_task(&ctx, task, None)
|
||||
} else if rx.is_empty() {
|
||||
if let Some(blocker) =
|
||||
blocker.wait_for(std::time::Duration::from_millis(50))
|
||||
{
|
||||
ctx.tx.send(Job::WaitingFor(blocker, task)).unwrap();
|
||||
} else {
|
||||
Self::do_task(&ctx, task, None)
|
||||
}
|
||||
} else {
|
||||
ctx.tx.send(Job::WaitingFor(blocker, task)).unwrap();
|
||||
}
|
||||
}
|
||||
Job::Awaited(blocker, task) => {
|
||||
Self::do_task(&ctx, task, Some(blocker.clone()));
|
||||
drop(blocker)
|
||||
}
|
||||
}
|
||||
|
||||
// if let Job::WithCond(blocker, job_inner) = job_msg {
|
||||
// if blocker.is_ready() {
|
||||
// job = Box::<Job>::into_inner(job_inner);
|
||||
// } else if rx.is_empty() {
|
||||
// if let Some(blocker) =
|
||||
// blocker.wait_for(std::time::Duration::from_millis(50))
|
||||
// {
|
||||
// job = Job::WithCond(blocker, job_inner);
|
||||
// } else {
|
||||
// job = Box::<Job>::into_inner(job_inner);
|
||||
// }
|
||||
// } else {
|
||||
// job = Job::WithCond(blocker, job_inner);
|
||||
// }
|
||||
// } else {
|
||||
// job = job_msg;
|
||||
// }
|
||||
|
||||
// match job {
|
||||
// Job::ParseFileMod { file, decl } => todo!(),
|
||||
// Job::OpenFile(path) => todo!(),
|
||||
// Job::WithCond(blocker, job) => {
|
||||
// ctx.tx.send(Job::WithCond(blocker, job)).unwrap()
|
||||
// }
|
||||
// }
|
||||
}
|
||||
});
|
||||
|
||||
Self {
|
||||
id,
|
||||
thread: thread_handle,
|
||||
}
|
||||
}
|
||||
|
||||
fn do_task(ctx: &WorkerCtx, task: Task, blocker: Option<WaitBlocker>) {
|
||||
match task {
|
||||
Task::ParseMod(_, _, _) => todo!(),
|
||||
Task::ParseFileMod0 { file, decl, path } => todo!(),
|
||||
Task::ParseFileMod1 {
|
||||
file,
|
||||
decl,
|
||||
path,
|
||||
ret,
|
||||
} => todo!(),
|
||||
Task::CompleteMod {
|
||||
ret_self,
|
||||
decl,
|
||||
body,
|
||||
path,
|
||||
child_mods,
|
||||
blocker,
|
||||
} => todo!(),
|
||||
Task::OpenFile(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
use std::{
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
/// like a WaitGroup from crossbeam, but can also just check if it's the last one
|
||||
pub struct WaitBlocker {
|
||||
inner: Arc<Inner>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
waiting: Mutex<usize>,
|
||||
cvar: Condvar,
|
||||
}
|
||||
|
||||
impl WaitBlocker {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
inner: Arc::new(Inner {
|
||||
waiting: Mutex::new(1),
|
||||
cvar: Condvar::new(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn wait(self) {
|
||||
if *self.inner.waiting.lock().unwrap() == 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let inner = self.inner.clone();
|
||||
drop(self);
|
||||
|
||||
inner
|
||||
.cvar
|
||||
.wait_while(inner.waiting.lock().unwrap(), |w| *w > 0);
|
||||
}
|
||||
|
||||
pub fn wait_for(self, dur: Duration) -> Option<Self> {
|
||||
if *self.inner.waiting.lock().unwrap() == 1 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let inner = self.inner.clone();
|
||||
drop(self);
|
||||
|
||||
let (_, timeout_res) = inner
|
||||
.cvar
|
||||
.wait_timeout_while(inner.waiting.lock().unwrap(), dur, |w| *w > 0)
|
||||
.unwrap();
|
||||
|
||||
if timeout_res.timed_out() {
|
||||
None
|
||||
} else {
|
||||
{
|
||||
let mut w = inner.waiting.lock().unwrap();
|
||||
*w += 1;
|
||||
}
|
||||
Some(Self { inner })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_ready(&self) -> bool {
|
||||
*self.inner.waiting.lock().unwrap() == 1
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for WaitBlocker {
|
||||
fn clone(&self) -> Self {
|
||||
let mut w = self.inner.waiting.lock().unwrap();
|
||||
*w += 1;
|
||||
drop(w);
|
||||
Self {
|
||||
inner: self.inner.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for WaitBlocker {
|
||||
fn drop(&mut self) {
|
||||
let mut w = self.inner.waiting.lock().unwrap();
|
||||
*w -= 1;
|
||||
if *w == 0 {
|
||||
self.inner.cvar.notify_all()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue