Parameter, local and global states
Parameter states
A system may request parameters when building:
#![allow(unused)] fn main() { #[system] fn hello_world( #[dynec(param)] counter: &mut i32, ) { *counter += 1; println!("{counter}"); } builder.schedule(hello_world.build(123)); builder.schedule(hello_world.build(456)); // ... world.execute(dynec::tracer::Noop); // prints 124 and 457 in unspecified order world.execute(dynec::tracer::Noop); // prints 125 and 458 in unspecified order }
The parameter type must be a reference (&T
or &mut T
) to the actual stored type.
Each #[dynec(param)]
parameter in hello_world
must be a reference (&T
or &mut T
),
adds a new parameter of type T
to the generated build()
method in the order they are specified,
with the reference part stripped.
Parameter states, along with other states, may be mutated when the system is run.
Each system (each instance returned by build()
) maintains its own states.
Local states
Unlike parameter states, local states are defined by the system itself
and is not specified through the build()
function.
#![allow(unused)] fn main() { #[system] fn hello_world( #[dynec(local(initial = 0))] counter: &mut i32, ) { *counter += 1; println!("{counter}"); } builder.schedule(hello_world.build()); builder.schedule(hello_world.build()); // ... world.execute(dynec::tracer::Noop); // prints 1, 1 in unspecified order world.execute(dynec::tracer::Noop); // prints 2, 2 in unspecified order }
0
is the initial value of counter
before the system is run the first time.
If parameter states are defined in the function,
the initial
expression may use such parameters by name as well.
Global states
States can also be shared among multiple systems
using the type as the identifier.
Such types must implement the Global
trait,
which can be done through the #[global]
macro:
#![allow(unused)] fn main() { #[derive(Default)] #[dynec::global(initial = Self::default())] struct MyCounter { value: i32, } #[system] fn add_counter( #[dynec(global)] counter: &mut MyCounter, ) { counter.value += 1; } #[system] fn print_counter( #[dynec(global)] counter: &MyCounter, ) { println!("{counter}"); } }
If no initial
value is specified in #[global]
,
the initial value of a global state must be assigned
in Bundle::register
.
#![allow(unused)] fn main() { impl world::Bundle for Bundle { fn register(&mut self, builder: &mut world::Builder) { builder.schedule(add_counter.build()); builder.schedule(print_counter.build()); builder.global(MyCounter { value: 123 }); } } // ... world.execute(dynec::tracer::Noop); // prints 123 or 124 based on unspecified order world.execute(dynec::tracer::Noop); // prints 124 or 125 based on unspecified order }
The program panics if some used global states do not have an initial
but Bundle::register
does not initialize them.
Note that &T
and &mut T
are semantically different for global states.
Multiple systems requesting &T
for the same T
may run in parallel
in a multi-threaded runtime,
but when a system requesting &mut T
is running,
all other systems requesting &T
or &mut T
cannot run until the system is complete
(but other unrelated systems can still be scheduled).