multithreading v2

This commit is contained in:
Mark 2023-10-28 13:48:46 +02:00
parent d72de43aad
commit cabbdc1764

View File

@ -6,14 +6,18 @@ use std::{
use crate::{ use crate::{
data::{self, Data, MersData, MersType, Type}, data::{self, Data, MersData, MersType, Type},
program::{self, run::CheckInfo}, program::{
self,
run::{CheckError, CheckInfo},
},
}; };
use super::Config; use super::Config;
impl Config { impl Config {
/// `thread: fn` turns `(func, arg)` into a `Thread`, which will run the function with the argument. /// `thread: fn` turns `func /* () -> t */` into a `Thread`, which will run the function.
/// `thread_get_result: fn` returns `()` while the thread is running and `(result)` otherwise. /// `thread_finished: fn` returns `false` while the thread is running and `true` otherwise.
/// `thread_await: fn` returns `t`, the value produced by the thread's function.
pub fn with_multithreading(self) -> Self { pub fn with_multithreading(self) -> Self {
self.add_type( self.add_type(
"Thread".to_string(), "Thread".to_string(),
@ -24,72 +28,85 @@ impl Config {
Data::new(data::function::Function { Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()), info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())), info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, i| todo!()), out: Arc::new(|a, _i| {
run: Arc::new(|a, _i| { let mut out = Type::empty();
let a = a.get(); for t in a.types.iter() {
if let (Some(f), Some(arg)) = ( if let Some(f) = t.as_any().downcast_ref::<data::function::FunctionT>() {
a.get(0).and_then(|v| { match (f.0)(&Type::empty_tuple()) {
v.get() Ok(t) => out.add(Arc::new(t)),
.as_any() Err(e) => return Err(CheckError::new().msg(format!("Can't call thread on a function which can't be called on an empty tuple: ")).err(e))
.downcast_ref::<data::function::Function>()
.cloned()
}),
a.get(1),
) {
Data::new(Thread(Arc::new(Mutex::new(Ok(std::thread::spawn(
move || f.run(arg),
))))))
} else {
unreachable!("thread called, but arg wasn't a (function, _)");
}
}),
}),
)
.add_var(
"thread_get_result".to_string(),
Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, i| todo!()),
run: Arc::new(|a, _i| {
let a = a.get();
if let Some(t) = a
.get(0)
.and_then(|v| v.get().as_any().downcast_ref::<Thread>().cloned())
{
let mut t = t.0.lock().unwrap();
if t.as_ref().is_ok_and(|t| t.is_finished()) {
unsafe {
// extract the JoinHandle from the Result by replacing it with uninitialized memory.
#[allow(invalid_value)]
let thread = std::mem::replace(
&mut *t,
std::mem::MaybeUninit::uninit().assume_init(),
)
.unwrap();
// forget about t and its uninitialized memory while replacing it with the new value
std::mem::forget(std::mem::replace(
&mut *t,
Err(thread.join().unwrap()),
));
} }
} }
match &*t { }
Ok(_) => Data::empty_tuple(), Ok(Type::new(ThreadT(out)))
Err(v) => Data::one_tuple(v.clone()), }),
} run: Arc::new(|a, _i| {
let a = a.get();
if let Some(f) = a
.as_any()
.downcast_ref::<data::function::Function>()
.cloned()
{
Data::new(Thread(Arc::new(Mutex::new(Ok(std::thread::spawn(
move || f.run(Data::empty_tuple()),
))))))
} else { } else {
unreachable!("thread_get_result called, but arg wasn't a Thread"); unreachable!("thread called, but arg wasn't a function");
} }
}), }),
}), }),
) )
.add_var("thread_finished".to_string(), Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| {
for t in a.types.iter() {
if !t.as_any().is::<ThreadT>() {
return Err(CheckError::new().msg(format!("Cannot call thread_finished on a value of type {t}, which isn't a thread but part of the argument {a}.")));
}
}
Ok(Type::new(data::bool::BoolT))
}),
run: Arc::new(|a, _i| {
let a = a.get();
let t = a.as_any().downcast_ref::<Thread>().unwrap().0.lock().unwrap();
Data::new(data::bool::Bool(match &*t {
Ok(t) => t.is_finished(),
Err(_d) => true,
}))
})
}))
.add_var("thread_await".to_string(), Data::new(data::function::Function {
info: Arc::new(program::run::Info::neverused()),
info_check: Arc::new(Mutex::new(CheckInfo::neverused())),
out: Arc::new(|a, _i| {
let mut out = Type::empty();
for t in a.types.iter() {
if let Some(t) = t.as_any().downcast_ref::<ThreadT>() {
out.add(Arc::new(Clone::clone(t)));
} else {
return Err(CheckError::new().msg(format!("Cannot call thread_await on a value of type {t}, which isn't a thread but part of the argument {a}.")));
}
}
Ok(out)
}),
run: Arc::new(|a, _i| {
let a = a.get();
let mut t = a.as_any().downcast_ref::<Thread>().unwrap().0.lock().unwrap();
let d = match std::mem::replace(&mut *t, Err(Data::empty_tuple())) {
Ok(t) => t.join().unwrap(),
Err(d) => d,
};
*t = Err(d.clone());
d
})
}))
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct Thread(Arc<Mutex<Result<JoinHandle<Data>, Data>>>); pub struct Thread(Arc<Mutex<Result<JoinHandle<Data>, Data>>>);
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct ThreadT(Type); pub struct ThreadT(Type);
impl MersData for Thread { impl MersData for Thread {