Add lock_update function for safer multithreading

&v = (v, 1).sum might fail if two threads do it at the same time or something, so you can now lock the reference to ensure no other thread messes with your data while you update its value
This commit is contained in:
Mark 2023-11-10 13:18:34 +01:00
parent 68d5b55c6f
commit 8bdd6e00e8
8 changed files with 67 additions and 20 deletions

View File

@ -92,8 +92,6 @@ fn main() {
std::process::exit(36); std::process::exit(36);
} }
}; };
#[cfg(debug_assertions)]
dbg!(&return_type);
if args.check == Check::Yes { if args.check == Check::Yes {
run.run(&mut info_run); run.run(&mut info_run);
} else { } else {

View File

@ -6,7 +6,7 @@ pub fn assign(from: &Data, target: &Data) {
.as_any() .as_any()
.downcast_ref::<crate::data::reference::Reference>() .downcast_ref::<crate::data::reference::Reference>()
{ {
*r.0.lock().unwrap() = from.clone(); *r.0.write().unwrap() = from.clone();
} else if let (Some(from), Some(target)) = ( } else if let (Some(from), Some(target)) = (
from.get() from.get()
.as_any() .as_any()

View File

@ -1,18 +1,18 @@
use std::{ use std::{
any::Any, any::Any,
fmt::Display, fmt::Display,
sync::{Arc, Mutex}, sync::{Arc, RwLock},
}; };
use super::{Data, MersData, MersType, Type}; use super::{Data, MersData, MersType, Type};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Reference(pub Arc<Mutex<Data>>); pub struct Reference(pub Arc<RwLock<Data>>);
impl MersData for Reference { impl MersData for Reference {
fn is_eq(&self, other: &dyn MersData) -> bool { fn is_eq(&self, other: &dyn MersData) -> bool {
if let Some(other) = other.as_any().downcast_ref::<Self>() { if let Some(other) = other.as_any().downcast_ref::<Self>() {
*other.0.lock().unwrap() == *self.0.lock().unwrap() *other.0.write().unwrap() == *self.0.write().unwrap()
} else { } else {
false false
} }
@ -21,7 +21,7 @@ impl MersData for Reference {
Box::new(Clone::clone(self)) Box::new(Clone::clone(self))
} }
fn as_type(&self) -> Type { fn as_type(&self) -> Type {
Type::new(ReferenceT(self.0.lock().unwrap().get().as_type())) Type::new(ReferenceT(self.0.write().unwrap().get().as_type()))
} }
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
@ -69,7 +69,7 @@ impl MersType for ReferenceT {
impl Display for Reference { impl Display for Reference {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "&{}", self.0.lock().unwrap().get()) write!(f, "&{}", self.0.write().unwrap().get())
} }
} }
impl Display for ReferenceT { impl Display for ReferenceT {

View File

@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, RwLock};
use crate::{ use crate::{
data::{Data, Type}, data::{Data, Type},
@ -69,7 +69,7 @@ impl Config {
pub fn add_var(mut self, name: String, val: Data) -> Self { pub fn add_var(mut self, name: String, val: Data) -> Self {
let t = val.get().as_type(); let t = val.get().as_type();
self.info_parsed.scopes[0].init_var(name, (0, self.globals)); self.info_parsed.scopes[0].init_var(name, (0, self.globals));
self.info_run.scopes[0].init_var(self.globals, Arc::new(Mutex::new(val))); self.info_run.scopes[0].init_var(self.globals, Arc::new(RwLock::new(val)));
self.info_check.scopes[0].init_var(self.globals, t); self.info_check.scopes[0].init_var(self.globals, t);
self.globals += 1; self.globals += 1;
self self

View File

@ -19,6 +19,7 @@ impl Config {
/// `len: fn` gets the length of strings or tuples /// `len: fn` gets the length of strings or tuples
/// `sleep: fn` sleeps for n seconds (pauses the current thread) /// `sleep: fn` sleeps for n seconds (pauses the current thread)
/// `panic: fn` exits the program with the given exit code /// `panic: fn` exits the program with the given exit code
/// `lock_update: fn` locks the value of a reference so you can exclusively modify it: &var.lock_update(v -> (v, 1).sum)
pub fn with_base(self) -> Self { pub fn with_base(self) -> Self {
self.add_var("try".to_string(), Data::new(data::function::Function { self.add_var("try".to_string(), Data::new(data::function::Function {
info: Arc::new(Info::neverused()), info: Arc::new(Info::neverused()),
@ -112,6 +113,54 @@ impl Config {
unreachable!("try: no function found") unreachable!("try: no function found")
}) })
})) }))
.add_var("lock_update".to_string(), Data::new(data::function::Function {
info: Arc::new(Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| {
for t in a.types.iter() {
if let Some(t) = t.as_any().downcast_ref::<data::tuple::TupleT>() {
if t.0.len() == 2 {
let arg_ref = &t.0[0];
if let Some(arg) = arg_ref.dereference() {
let func = &t.0[1];
for func_t in func.types.iter() {
if let Some(f) = func_t.as_any().downcast_ref::<data::function::FunctionT>() {
match (f.0)(&arg) {
Ok(out) => {
if !out.is_included_in(&arg) {
return Err(format!("Function returns a value of type {out}, which isn't included in the type of the reference, {arg}.").into());
}
},
Err(e) => return Err(CheckError::new().msg(format!("Invalid argument type {arg} for function")).err(e)),
}
} else {
return Err(format!("Arguments must be (reference, function)").into());
}
}
} else {
return Err(format!("Arguments must be (reference, function), but {arg_ref} isn't a reference").into());
}
} else {
return Err(format!("Can't call lock_update on tuple type {t} with length != 2, which is part of the argument type {a}.").into());
}
} else {
return Err(format!("Can't call lock_update on non-tuple type {t}, which is part of the argument type {a}.").into());
}
}
Ok(Type::empty_tuple())
}),
run: Arc::new(|a, _i| {
let a = a.get();
let a = a.as_any().downcast_ref::<data::tuple::Tuple>().unwrap();
let arg_ref = a.0[0].get();
let arg_ref = arg_ref.as_any().downcast_ref::<data::reference::Reference>().unwrap();
let mut arg = arg_ref.0.write().unwrap();
let func = a.0[1].get();
let func = func.as_any().downcast_ref::<data::function::Function>().unwrap();
*arg = func.run(arg.clone());
Data::empty_tuple()
})
}))
.add_var("sleep".to_string(), Data::new(data::function::Function { .add_var("sleep".to_string(), Data::new(data::function::Function {
info: Arc::new(Info::neverused()), info: Arc::new(Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
@ -268,7 +317,7 @@ impl Config {
.as_any() .as_any()
.downcast_ref::<data::reference::Reference>() .downcast_ref::<data::reference::Reference>()
{ {
r.0.lock().unwrap().clone() r.0.write().unwrap().clone()
} else { } else {
unreachable!("called deref on non-reference") unreachable!("called deref on non-reference")
} }

View File

@ -53,7 +53,7 @@ impl Config {
.downcast_ref::<data::reference::Reference>() .downcast_ref::<data::reference::Reference>()
.unwrap() .unwrap()
.0 .0
.lock() .write()
.unwrap() .unwrap()
.get_mut() .get_mut()
.mut_any() .mut_any()
@ -118,7 +118,7 @@ impl Config {
.downcast_ref::<data::reference::Reference>() .downcast_ref::<data::reference::Reference>()
.unwrap() .unwrap()
.0 .0
.lock() .write()
.unwrap() .unwrap()
.get_mut() .get_mut()
.mut_any() .mut_any()

View File

@ -1,6 +1,6 @@
use std::{ use std::{
fmt::{Debug, Display}, fmt::{Debug, Display},
sync::{Arc, Mutex}, sync::{Arc, RwLock},
}; };
use colored::Colorize; use colored::Colorize;
@ -240,7 +240,7 @@ pub type CheckInfo = info::Info<CheckLocal>;
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct Local { pub struct Local {
vars: Vec<Arc<Mutex<Data>>>, vars: Vec<Arc<RwLock<Data>>>,
} }
#[derive(Default, Clone, Debug)] #[derive(Default, Clone, Debug)]
pub struct CheckLocal { pub struct CheckLocal {
@ -248,9 +248,9 @@ pub struct CheckLocal {
} }
impl info::Local for Local { impl info::Local for Local {
type VariableIdentifier = usize; type VariableIdentifier = usize;
type VariableData = Arc<Mutex<Data>>; type VariableData = Arc<RwLock<Data>>;
fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) { fn init_var(&mut self, id: Self::VariableIdentifier, value: Self::VariableData) {
let nothing = Arc::new(Mutex::new(Data::new(data::bool::Bool(false)))); let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false))));
while self.vars.len() <= id { while self.vars.len() <= id {
self.vars.push(Arc::clone(&nothing)); self.vars.push(Arc::clone(&nothing));
} }

View File

@ -1,4 +1,4 @@
use std::sync::{Arc, Mutex}; use std::sync::{Arc, RwLock};
use crate::data::{self, Data, Type}; use crate::data::{self, Data, Type};
@ -40,7 +40,7 @@ impl MersStatement for Variable {
} }
fn run_custom(&self, info: &mut super::Info) -> Data { fn run_custom(&self, info: &mut super::Info) -> Data {
if self.is_init { if self.is_init {
let nothing = Arc::new(Mutex::new(Data::new(data::bool::Bool(false)))); let nothing = Arc::new(RwLock::new(Data::new(data::bool::Bool(false))));
while info.scopes[self.var.0].vars.len() <= self.var.1 { while info.scopes[self.var.0].vars.len() <= self.var.1 {
info.scopes[self.var.0].vars.push(Arc::clone(&nothing)); info.scopes[self.var.0].vars.push(Arc::clone(&nothing));
} }
@ -52,7 +52,7 @@ impl MersStatement for Variable {
))) )))
} else { } else {
info.scopes[self.var.0].vars[self.var.1] info.scopes[self.var.0].vars[self.var.1]
.lock() .write()
.unwrap() .unwrap()
.clone() .clone()
} }