-
Notifications
You must be signed in to change notification settings - Fork 91
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add book sections on multithreading and hot reloading
- Loading branch information
Showing
8 changed files
with
377 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
# Hot reloading | ||
|
||
Compiling a [`Unit`] and a [`RuntimeContext`] are expensive operations compared | ||
to the cost of calling a function. So you should try to do this as little as | ||
possible. It is appropriate to recompile a script when the source of the script | ||
changes. This section provides you with details for how this can be done when | ||
loading scripts from the filesystem. | ||
|
||
A typical way to accomplish this is to watch a scripts directory using the | ||
[`notify` crate]. This allow the application to generate events whenever changes | ||
to the directory are detected. See the [`hot_reloading` example] and in | ||
particular the [`PathReloader`] type. | ||
|
||
``` | ||
{{#include ../../examples/examples/hot_reloading.rs}} | ||
``` | ||
|
||
[`notify` crate]: https://docs.rs/notify | ||
[`Unit`]: https://docs.rs/rune/latest/rune/runtime/unit/struct.Unit.html | ||
[`hot_reloading` example]: https://github.com/rune-rs/rune/blob/main/examples/examples/hot_reloading.rs | ||
[`PathReloader`]: https://github.com/rune-rs/rune/blob/main/examples/examples/hot_reloading/path_reloader.rs | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
# Multithreading | ||
|
||
Rune is thread safe, but the [`Vm`] does not implement `Sync` so cannot directly | ||
be shared across threads. This section details instead how you are intended to | ||
use Rune in a multithreaded environment. | ||
|
||
Compiling a [`Unit`] and a [`RuntimeContext`] are expensive operations compared | ||
to the cost of calling a function. So you should try to do this as little as | ||
possible. It is appropriate to recompile a script when the source of the script | ||
changes. See the [Hot reloading] section for more information on this. | ||
|
||
Once you have a `Unit` and a `RuntimeContext` they are thread safe and can be | ||
used by multiple threads simultaneously through `Arc<Unit>` and | ||
`Arc<RuntimeContext>`. Constructing a `Vm` with these through `Vm::new` is a | ||
very cheap operation. | ||
|
||
> `Vm`'s do allocate a stack, to avoid this you'd have to employ even more | ||
> advanced techniques, such as storing the virtual machine in a thread local and | ||
> using it by swapping out the `Unit` and `RuntimeContext` associated with it | ||
> through [`Vm::unit_mut`] and [`Vm::context_mut`] respectively. | ||
```rust | ||
let unit: Arc<Unit> = /* todo */; | ||
let context: Arc<RuntimeContext> = /* todo */; | ||
|
||
std::thread::spawn(move || { | ||
let mut vm = Vm::new(unit, context); | ||
let value = vm.call(["function"], (42,))?; | ||
Ok(()) | ||
}); | ||
``` | ||
|
||
Using [`Vm::send_execute`] is a way to assert that a given execution is thread | ||
safe. And allows you to use Rune in asynchronous multithreaded environments, | ||
such as Tokio. This is achieved by ensuring that all captured arguments are | ||
[`ConstValue`]'s, which in contrast to [`Value`]'s are guaranteed to be | ||
thread-safe: | ||
|
||
``` | ||
{{#include ../../examples/examples/tokio_spawn.rs}} | ||
``` | ||
|
||
Finally [`Function::into_sync`] exists to coerce a function into a | ||
[`SyncFunction], which is a thread-safe variant of a regular [`Function`]. This | ||
is a fallible operation since all values which are captured in the function-type | ||
in case its a closure has to be coerced to [`ConstValue`]. If this is not the | ||
case, the conversion will fail. | ||
|
||
[`ConstValue`]: https://docs.rs/rune/latest/rune/runtime/enum.ConstValue.html | ||
[`Function::into_sync`]: https://docs.rs/rune/latest/rune/runtime/struct.Function.html#method.into_sync | ||
[`Function`]: https://docs.rs/rune/latest/rune/runtime/struct.Function.html | ||
[`notify`]: https://docs.rs/notify | ||
[`RuntimeContext`]: https://docs.rs/rune/latest/rune/runtime/struct.RuntimeContext.html | ||
[`SyncFunction`]: https://docs.rs/rune/latest/rune/runtime/struct.SyncFunction.html | ||
[`Unit`]: https://docs.rs/rune/latest/rune/runtime/struct.Unit.html | ||
[`Value`]: https://docs.rs/rune/latest/rune/runtime/enum.Value.html | ||
[`Vm::context_mut`]: https://docs.rs/rune/latest/rune/runtime/struct.Vm.html#method.context_mut | ||
[`Vm::send_execute`]: https://docs.rs/rune/latest/rune/runtime/struct.Vm.html#method.send_execute | ||
[`Vm::unit_mut`]: https://docs.rs/rune/latest/rune/runtime/struct.Vm.html#method.unit_mut | ||
[`Vm`]: https://docs.rs/rune/latest/rune/runtime/struct.Vm.html | ||
[Hot reloading]: ./hot_reloading.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
#[path = "hot_reloading/path_reloader.rs"] | ||
mod path_reloader; | ||
|
||
use std::path::PathBuf; | ||
use std::pin::pin; | ||
use std::sync::Arc; | ||
|
||
use anyhow::{Context as _, Result}; | ||
use rune::{Context, Vm}; | ||
|
||
#[tokio::main] | ||
async fn main() -> Result<()> { | ||
let root = | ||
PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").context("missing CARGO_MANIFEST_DIR")?); | ||
|
||
let context = Context::with_default_modules()?; | ||
|
||
let mut exit = pin!(tokio::signal::ctrl_c()); | ||
let mut reloader = pin!(path_reloader::PathReloader::new( | ||
root.join("scripts"), | ||
&context | ||
)?); | ||
|
||
let context = Arc::new(context.runtime()?); | ||
|
||
let mut events = Vec::new(); | ||
|
||
loop { | ||
tokio::select! { | ||
_ = exit.as_mut() => { | ||
break; | ||
} | ||
result = reloader.as_mut().watch(&mut events) => { | ||
result?; | ||
} | ||
} | ||
|
||
for event in events.drain(..) { | ||
match event { | ||
path_reloader::PathEvent::Added(path, unit) => { | ||
let mut vm = Vm::new(context.clone(), unit); | ||
|
||
if let Err(error) = vm.call(["hello"], ()) { | ||
println!("Error: {}", error); | ||
} | ||
} | ||
path_reloader::PathEvent::Removed(path, unit) => { | ||
let mut vm = Vm::new(context.clone(), unit); | ||
|
||
if let Err(error) = vm.call(["goodbye"], ()) { | ||
println!("Error: {}", error); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
Ok(()) | ||
} |
Oops, something went wrong.