Add sync
feature to make Dynamic, Scope and AST Send + Sync
.
This commit is contained in:
parent
2c86abc58c
commit
0873bdc152
@ -32,6 +32,7 @@ no_optimize = [] # no script optimizer
|
||||
optimize_full = [] # set optimization level to Full (default is Simple) - this is a feature used only to simplify testing
|
||||
only_i32 = [] # set INT=i32 (useful for 32-bit systems)
|
||||
only_i64 = [] # set INT=i64 (default) and disable support for all other integer types
|
||||
sync = [] # restrict to only types that implement Send + Sync
|
||||
|
||||
# compiling for no-std
|
||||
no_std = [ "num-traits/libm", "hashbrown", "core-error", "libm" ]
|
||||
|
11
README.md
11
README.md
@ -68,6 +68,7 @@ Optional features
|
||||
| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. |
|
||||
| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. |
|
||||
| `no_std` | Build for `no-std`. Notice that additional dependencies will be pulled in to replace `std` features. |
|
||||
| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, [`Scope`] and `AST` are both `Send + Sync`. |
|
||||
|
||||
By default, Rhai includes all the standard functionalities in a small, tight package. Most features are here to opt-**out** of certain functionalities that are not needed.
|
||||
Excluding unneeded functionalities can result in smaller, faster builds as well as less bugs due to a more restricted language.
|
||||
@ -82,6 +83,7 @@ Excluding unneeded functionalities can result in smaller, faster builds as well
|
||||
[`only_i32`]: #optional-features
|
||||
[`only_i64`]: #optional-features
|
||||
[`no_std`]: #optional-features
|
||||
[`sync`]: #optional-features
|
||||
|
||||
Related
|
||||
-------
|
||||
@ -312,12 +314,12 @@ if type_of(x) == "string" {
|
||||
}
|
||||
```
|
||||
|
||||
Dynamic values
|
||||
--------------
|
||||
`Dynamic` values
|
||||
----------------
|
||||
|
||||
[`Dynamic`]: #dynamic-values
|
||||
|
||||
A `Dynamic` value can be _any_ type.
|
||||
A `Dynamic` value can be _any_ type. However, if the [`sync`] feature is used, then all types must be `Send + Sync`.
|
||||
|
||||
Because [`type_of()`] a `Dynamic` value returns the type of the actual value, it is usually used to perform type-specific
|
||||
actions based on the actual value's type.
|
||||
@ -704,6 +706,9 @@ By default, Rhai treats each [`Engine`] invocation as a fresh one, persisting on
|
||||
This gives each evaluation a clean starting slate. In order to continue using the same global state from one invocation to the next,
|
||||
such a state must be manually created and passed in.
|
||||
|
||||
All `Scope` variables are [`Dynamic`], meaning they can store values of any type. If the [`sync`] feature is used, however, then only types
|
||||
that are `Send + Sync` are supported, and the entire `Scope` itself will also be `Send + Sync`. This is extremely useful in multi-threaded applications.
|
||||
|
||||
In this example, a global state object (a `Scope`) is created with a few initialized variables, then the same state is threaded through multiple invocations:
|
||||
|
||||
```rust
|
||||
|
43
src/any.rs
43
src/any.rs
@ -1,7 +1,7 @@
|
||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||
|
||||
use crate::stdlib::{
|
||||
any::{type_name, Any as StdAny, TypeId},
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
fmt,
|
||||
};
|
||||
@ -13,7 +13,8 @@ pub type Variant = dyn Any;
|
||||
pub type Dynamic = Box<Variant>;
|
||||
|
||||
/// A trait covering any type.
|
||||
pub trait Any: StdAny {
|
||||
#[cfg(feature = "sync")]
|
||||
pub trait Any: crate::stdlib::any::Any + Send + Sync {
|
||||
/// Get the `TypeId` of this type.
|
||||
fn type_id(&self) -> TypeId;
|
||||
|
||||
@ -28,7 +29,43 @@ pub trait Any: StdAny {
|
||||
fn _closed(&self) -> _Private;
|
||||
}
|
||||
|
||||
impl<T: Clone + StdAny + ?Sized> Any for T {
|
||||
#[cfg(feature = "sync")]
|
||||
impl<T: crate::stdlib::any::Any + Clone + Send + Sync + ?Sized> Any for T {
|
||||
fn type_id(&self) -> TypeId {
|
||||
TypeId::of::<T>()
|
||||
}
|
||||
|
||||
fn type_name(&self) -> &'static str {
|
||||
type_name::<T>()
|
||||
}
|
||||
|
||||
fn into_dynamic(&self) -> Dynamic {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
|
||||
fn _closed(&self) -> _Private {
|
||||
_Private
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
pub trait Any: crate::stdlib::any::Any {
|
||||
/// Get the `TypeId` of this type.
|
||||
fn type_id(&self) -> TypeId;
|
||||
|
||||
/// Get the name of this type.
|
||||
fn type_name(&self) -> &'static str;
|
||||
|
||||
/// Convert into `Dynamic`.
|
||||
fn into_dynamic(&self) -> Dynamic;
|
||||
|
||||
/// This trait may only be implemented by `rhai`.
|
||||
#[doc(hidden)]
|
||||
fn _closed(&self) -> _Private;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "sync"))]
|
||||
impl<T: crate::stdlib::any::Any + Clone + ?Sized> Any for T {
|
||||
fn type_id(&self) -> TypeId {
|
||||
TypeId::of::<T>()
|
||||
}
|
||||
|
@ -2,8 +2,7 @@
|
||||
|
||||
///! This test simulates an external command object that is driven by a script.
|
||||
use rhai::{Engine, EvalAltResult, RegisterFn, Scope, INT};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
/// External command.
|
||||
struct Command {
|
||||
@ -24,19 +23,19 @@ impl Command {
|
||||
/// Wrapper object to wrap a command object.
|
||||
#[derive(Clone)]
|
||||
struct CommandWrapper {
|
||||
command: Rc<RefCell<Command>>,
|
||||
command: Arc<Mutex<Command>>,
|
||||
}
|
||||
|
||||
impl CommandWrapper {
|
||||
/// Delegate command action.
|
||||
pub fn do_action(&mut self, x: i64) {
|
||||
let mut command = self.command.borrow_mut();
|
||||
let mut command = self.command.lock().unwrap();
|
||||
let val = command.get();
|
||||
command.action(val + x);
|
||||
}
|
||||
/// Delegate get value action.
|
||||
pub fn get_value(&mut self) -> i64 {
|
||||
let command = self.command.borrow();
|
||||
let command = self.command.lock().unwrap();
|
||||
command.get()
|
||||
}
|
||||
}
|
||||
@ -47,8 +46,8 @@ fn test_side_effects() -> Result<(), EvalAltResult> {
|
||||
let mut scope = Scope::new();
|
||||
|
||||
// Create the command object with initial state, handled by an `Rc`.
|
||||
let command = Rc::new(RefCell::new(Command { state: 12 }));
|
||||
assert_eq!(command.borrow().get(), 12);
|
||||
let command = Arc::new(Mutex::new(Command { state: 12 }));
|
||||
assert_eq!(command.lock().unwrap().get(), 12);
|
||||
|
||||
// Create the wrapper.
|
||||
let wrapper = CommandWrapper {
|
||||
@ -76,7 +75,7 @@ fn test_side_effects() -> Result<(), EvalAltResult> {
|
||||
);
|
||||
|
||||
// Make sure the actions are properly performed
|
||||
assert_eq!(command.borrow().get(), 42);
|
||||
assert_eq!(command.lock().unwrap().get(), 42);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user