Skip to content

Commit 7f4fdd1

Browse files
authored
Merge pull request #68 from frame-lang/async-compatible
Add thread_safe feature to generate Send+Sync compatible state machines
2 parents 4f0fabe + 959453f commit 7f4fdd1

30 files changed

+4198
-1760
lines changed

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frame_runtime/src/env.rs

Lines changed: 64 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,54 +4,48 @@
44
use std::any::Any;
55
use std::cell::{Ref, RefCell};
66
use std::rc::Rc;
7+
use std::sync::{Arc, Mutex};
78

89
/// Environments associate names (i.e. variables/parameters) with values.
910
///
1011
/// Environments are defined as a trait rather than a simple HashMap so that we can directly reuse
1112
/// the various structs generated by Frame rather than constructing a bunch of parallel data
1213
/// structures each time we query the state of the machine.
13-
///
14+
///
1415
/// It is common for a particular environment to be absent because there are no variables of the
1516
/// kind held by that environment. We represent absent environments by an empty environment rather
1617
/// than using an `Option` type because it simplifies the interface and because the distinction
17-
/// between `None` and `Some(empty)` is not significant.
18+
/// between `None` and `Some(Empty)` is not significant.
1819
pub trait Environment {
1920
/// Is this the empty environment?
2021
fn is_empty(&self) -> bool {
2122
false
2223
}
2324
/// Get the value associated with a name.
24-
fn lookup(&self, name: &str) -> Option<&dyn Any>;
25+
fn lookup(&self, name: &str) -> Option<Box<dyn Any>>;
2526
}
2627

2728
/// The trivial empty enviorment. This can be used in place of an environment when that environment
2829
/// is absent.
2930
#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
30-
pub struct Empty {}
31+
pub struct Empty;
3132

3233
impl Empty {
33-
/// Create a new empty environment.
34-
pub fn new() -> Empty {
35-
Empty {}
36-
}
37-
38-
/// Create a new reference-counted pointer to an empty environment.
39-
pub fn new_rc() -> Rc<Empty> {
40-
Rc::new(Empty::new())
34+
/// Get an `Arc` reference to the empty environment.
35+
pub fn arc() -> Arc<Empty> {
36+
Arc::new(Empty)
4137
}
42-
}
43-
44-
impl Default for Empty {
45-
fn default() -> Self {
46-
Empty::new()
38+
/// Get an `Rc` reference to the empty environment.
39+
pub fn rc() -> Rc<Empty> {
40+
Rc::new(Empty)
4741
}
4842
}
4943

5044
impl Environment for Empty {
5145
fn is_empty(&self) -> bool {
5246
true
5347
}
54-
fn lookup(&self, _name: &str) -> Option<&dyn Any> {
48+
fn lookup(&self, _name: &str) -> Option<Box<dyn Any>> {
5549
None
5650
}
5751
}
@@ -60,7 +54,7 @@ impl<'a, T: Environment> Environment for Ref<'a, T> {
6054
fn is_empty(&self) -> bool {
6155
(**self).is_empty()
6256
}
63-
fn lookup(&self, name: &str) -> Option<&dyn Any> {
57+
fn lookup(&self, name: &str) -> Option<Box<dyn Any>> {
6458
(**self).lookup(name)
6559
}
6660
}
@@ -69,15 +63,46 @@ impl<T: Environment> Environment for RefCell<T> {
6963
fn is_empty(&self) -> bool {
7064
self.borrow().is_empty()
7165
}
72-
fn lookup(&self, name: &str) -> Option<&dyn Any> {
66+
fn lookup(&self, name: &str) -> Option<Box<dyn Any>> {
7367
unsafe { (*self.as_ptr()).lookup(name) }
7468
}
7569
}
7670

71+
impl<T: Environment + Clone> Environment for Mutex<T> {
72+
fn is_empty(&self) -> bool {
73+
self.lock().unwrap().is_empty()
74+
}
75+
fn lookup(&self, name: &str) -> Option<Box<dyn Any>> {
76+
// TODO: This could be super slow since it needs to clone the whole Args/Vars type...
77+
// Probably need to re-think the whole Environment trait to get an efficient implementation
78+
// for the `sync` case... :-(
79+
self.lock().unwrap().clone().lookup(name)
80+
}
81+
}
82+
83+
/// Definitions specific to the synchronized/thread-safe interface.
84+
pub mod sync {
85+
pub use super::*;
86+
use std::sync::Arc;
87+
88+
/// A reference-counted pointer to an environment.
89+
pub type EnvironmentPtr = Arc<dyn super::Environment>;
90+
}
91+
92+
/// Definitions specific to the unsynchronized interface.
93+
pub mod unsync {
94+
pub use super::*;
95+
use std::rc::Rc;
96+
97+
/// A reference-counted pointer to an environment.
98+
pub type EnvironmentPtr = Rc<dyn super::Environment>;
99+
}
100+
77101
#[allow(clippy::approx_constant)]
78102
#[cfg(test)]
79103
mod tests {
80104
use super::*;
105+
use std::any::Any;
81106

82107
struct TestArgs {
83108
x: i32,
@@ -86,28 +111,26 @@ mod tests {
86111
}
87112

88113
impl Environment for TestArgs {
89-
fn lookup(&self, name: &str) -> Option<&dyn Any> {
114+
fn lookup(&self, name: &str) -> Option<Box<dyn Any>> {
90115
match name {
91-
"x" => Some(&self.x),
92-
"y" => Some(&self.y),
93-
"z" => Some(&self.z),
116+
"x" => Some(Box::new(self.x)),
117+
"y" => Some(Box::new(self.y)),
118+
"z" => Some(Box::new(self.z)),
94119
_ => None,
95120
}
96121
}
97122
}
98123

99124
#[test]
100125
fn empty_environment_is_empty() {
101-
let empty = Empty::new();
102-
assert!(empty.is_empty());
126+
assert!(Empty.is_empty());
103127
}
104128

105129
#[test]
106130
fn empty_environment_returns_none() {
107-
let empty = Empty::new();
108-
assert!(empty.lookup("x").is_none());
109-
assert!(empty.lookup("y").is_none());
110-
assert!(empty.lookup("z").is_none());
131+
assert!(Empty.lookup("x").is_none());
132+
assert!(Empty.lookup("y").is_none());
133+
assert!(Empty.lookup("z").is_none());
111134
}
112135

113136
#[test]
@@ -130,19 +153,22 @@ mod tests {
130153

131154
let opt_x = args.lookup("x");
132155
assert!(opt_x.is_some());
133-
let opt_i32 = opt_x.unwrap().downcast_ref::<i32>();
156+
let x = opt_x.unwrap();
157+
let opt_i32 = x.downcast_ref::<i32>();
134158
assert!(opt_i32.is_some());
135159
assert_eq!(*opt_i32.unwrap(), 42);
136160

137161
let opt_y = args.lookup("y");
138162
assert!(opt_y.is_some());
139-
let opt_bool = opt_y.unwrap().downcast_ref::<bool>();
163+
let y = opt_y.unwrap();
164+
let opt_bool = y.downcast_ref::<bool>();
140165
assert!(opt_bool.is_some());
141166
assert!(!*opt_bool.unwrap());
142167

143-
let opt_y = args.lookup("z");
144-
assert!(opt_y.is_some());
145-
let opt_bool = opt_y.unwrap().downcast_ref::<Option<f32>>();
168+
let opt_z = args.lookup("z");
169+
assert!(opt_z.is_some());
170+
let z = opt_z.unwrap();
171+
let opt_bool = z.downcast_ref::<Option<f32>>();
146172
assert!(opt_bool.is_some());
147173
assert_eq!(*opt_bool.unwrap(), Some(3.14));
148174
}
@@ -164,8 +190,9 @@ mod tests {
164190

165191
let opt_z = args.lookup("z");
166192
assert!(opt_z.is_some());
167-
assert!(opt_y.unwrap().downcast_ref::<f32>().is_none());
168-
assert!(opt_y.unwrap().downcast_ref::<Option<i32>>().is_none());
193+
let z = opt_z.unwrap();
194+
assert!(z.downcast_ref::<f32>().is_none());
195+
assert!(z.downcast_ref::<Option<i32>>().is_none());
169196
}
170197

171198
#[test]

0 commit comments

Comments
 (0)