onion_vm/types/
thread_handle.rs1use std::{
2 any::Any,
3 collections::VecDeque,
4 fmt::{Debug, Formatter},
5 sync::{Arc, Mutex},
6 thread::JoinHandle,
7};
8
9use arc_gc::{arc::GCArc, arc::GCArcWeak, traceable::GCTraceable};
10
11use crate::{lambda::runnable::RuntimeError, types::object::OnionStaticObject};
12
13use super::object::{OnionObject, OnionObjectCell, OnionObjectExt};
14
15pub struct OnionThreadHandle {
17 handle: Mutex<Option<JoinHandle<Result<Box<OnionStaticObject>, RuntimeError>>>>,
18 is_finished: Mutex<bool>,
19 cached_result: Mutex<Option<Result<OnionStaticObject, RuntimeError>>>,
20}
21
22impl OnionThreadHandle {
23 pub fn new(handle: JoinHandle<Result<Box<OnionStaticObject>, RuntimeError>>) -> Self {
24 Self {
25 handle: Mutex::new(Some(handle)),
26 is_finished: Mutex::new(false),
27 cached_result: Mutex::new(None),
28 }
29 }
30 pub fn is_finished(&self) -> bool {
32 *self.is_finished.lock().unwrap() || self.handle.lock().unwrap().is_none()
33 }
34}
35
36impl Clone for OnionThreadHandle {
37 fn clone(&self) -> Self {
38 Self {
40 handle: Mutex::new(None),
41 is_finished: Mutex::new(*self.is_finished.lock().unwrap()),
42 cached_result: Mutex::new(self.cached_result.lock().unwrap().clone()),
43 }
44 }
45}
46
47impl Debug for OnionThreadHandle {
48 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
49 write!(
50 f,
51 "OnionThreadHandle(finished: {}, has_handle: {})",
52 self.is_finished(),
53 self.handle.lock().unwrap().is_some()
54 )
55 }
56}
57
58impl GCTraceable<OnionObjectCell> for OnionThreadHandle {
59 fn collect(&self, _queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
60 }
62}
63
64impl OnionObjectExt for OnionThreadHandle {
65 fn as_any(&self) -> &dyn Any {
66 self
67 }
68
69 fn repr(&self, _ptrs: &Vec<*const OnionObject>) -> Result<String, RuntimeError> {
70 Ok(format!("ThreadHandle(finished: {})", self.is_finished()))
71 }
72
73 fn equals(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
74 if let OnionObject::Custom(other_custom) = other {
76 if let Some(_other_handle) = other_custom.as_any().downcast_ref::<OnionThreadHandle>() {
77 Ok(false)
80 } else {
81 Ok(false)
82 }
83 } else {
84 Ok(false)
85 }
86 }
87
88 fn upgrade(&self, _collected: &mut Vec<GCArc<OnionObjectCell>>) {
89 }
91
92 fn reconstruct_container(&self) -> Result<OnionObject, RuntimeError> {
93 Ok(OnionObject::Custom(Arc::new(self.clone())))
94 }
95
96 fn is_same(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
97 if let OnionObject::Custom(other_custom) = other {
98 if let Some(other_handle) = other_custom.as_any().downcast_ref::<OnionThreadHandle>() {
99 Ok(std::ptr::eq(self, other_handle))
101 } else {
102 Ok(false)
103 }
104 } else {
105 Ok(false)
106 }
107 }
108
109 fn to_boolean(&self) -> Result<bool, RuntimeError> {
110 Ok(!self.is_finished())
112 }
113
114 fn type_of(&self) -> Result<String, RuntimeError> {
115 Ok("ThreadHandle".to_string())
116 }
117
118 fn value_of(&self) -> Result<OnionStaticObject, RuntimeError> {
119 if let Some(ref cached) = *self.cached_result.lock().unwrap() {
121 return cached.clone();
122 }
123
124 let mut handle_guard = self.handle.lock().unwrap();
126 if let Some(handle) = handle_guard.take() {
127 *self.is_finished.lock().unwrap() = true;
128 drop(handle_guard); let result = match handle.join() {
131 Ok(result) => match result {
132 Ok(object) => Ok(object.as_ref().clone()),
133 Err(err) => Err(err),
134 },
135 Err(_) => Err(RuntimeError::DetailedError(
136 "Thread panicked during execution".to_string().into(),
137 )),
138 };
139
140 *self.cached_result.lock().unwrap() = Some(result.clone());
142 result
143 } else {
144 Err(RuntimeError::DetailedError(
145 "Thread has already finished".to_string().into(),
146 ))
147 }
148 }
149
150 fn with_attribute(
151 &self,
152 key: &OnionObject,
153 f: &mut dyn FnMut(&OnionObject) -> Result<(), RuntimeError>,
154 ) -> Result<(), RuntimeError> {
155 match key {
156 OnionObject::String(s) => match s.as_str() {
157 "is_finished" => f(&OnionObject::Boolean(self.is_finished())),
158 "has_handle" => {
159 let has_handle = self.handle.lock().unwrap().is_some();
160 f(&OnionObject::Boolean(has_handle))
161 }
162 "has_result" => {
163 let has_result = self.cached_result.lock().unwrap().is_some();
164 f(&OnionObject::Boolean(has_result))
165 }
166 "is_success" => {
167 if let Some(ref cached) = *self.cached_result.lock().unwrap() {
168 f(&OnionObject::Boolean(cached.is_ok()))
169 } else {
170 f(&OnionObject::Boolean(false)) }
172 }
173 "is_error" => {
174 if let Some(ref cached) = *self.cached_result.lock().unwrap() {
175 f(&OnionObject::Boolean(cached.is_err()))
176 } else {
177 f(&OnionObject::Boolean(false)) }
179 }
180 "error" => {
181 if let Some(ref cached) = *self.cached_result.lock().unwrap() {
183 match cached {
184 Ok(_) => f(&OnionObject::Null),
185 Err(err) => f(&OnionObject::String(format!("{}", err).into())),
186 }
187 } else {
188 f(&OnionObject::Null)
189 }
190 }
191 _ => Err(RuntimeError::InvalidOperation(
192 format!("Attribute '{}' not found in ThreadHandle", s).into(),
193 )),
194 },
195 _ => Err(RuntimeError::InvalidOperation(
196 format!("Attribute {:?} not found in ThreadHandle", key).into(),
197 )),
198 }
199 }
200}