Attribute Macro interthread::group
source · #[group]Expand description
§A set of actors sharing a single thread
In the realm of concurrent programming, creating a separate thread
for each individual actor can sometimes incur a significant overhead.
In such scenarios, developers may opt to populate an actor’s
definition with complex encapsulations (potential actors),
effectively creating a collection of objects running within
a single thread. While this approach proves resource-efficient,
it does come with a trade-off: accessing the methods of field
types must be explicitly written within the main actor
implementation block.
This is where the group macro comes into play. A group is a
set of actors that share a single thread, consisting of
a main-actor and group-actors contained within the
main-actor’s fields. Developers can now bypass the need to rewrite
method wrappers, gaining direct access to group-actor methods via
dot notation, as seamlessly as if these group-actors were actors
in their own right.
In this scenario, the methods of the main-actor take on the responsibility
for interaction within and between group-actors, while the latter primarily
serve to export their functionality.
Before delving into further details, let’s explore an example of a group.
Assuming there is a good understanding of how an actor operates, once the
Live instance is returned after invoking the new method, the actor is
already running in a separate thread. Therefore, there’s no need to
complicate the example with extra thread spawning just for visual clarity.
§Examples
pub struct Aa(u8);
impl Aa {
pub fn add(&mut self, v: u8){
self.0 += v;
}
}
pub struct Bb(u8);
impl Bb {
pub fn add(&mut self, v: u8){
self.0 += v;
}
}
pub struct AaBb {
pub a: Aa,
pub b: Bb,
}
#[interthread::group( file= "path/to/file.rs")]
impl AaBb {
pub fn new( ) -> Self {
let a = Aa(0);
let b = Bb(0);
Self{ a,b}
}
pub fn add(&mut self, v:u8){
self.a.0 += v;
self.b.0 += v;
}
pub fn get_value(&mut self) -> (u8,u8) {
(self.a.0,self.b.0)
}
}
pub fn main(){
let mut group = AaBbGroupLive::new();
// access to group method
group.add(1);
assert_eq!((1,1),group.get_value());
// access to field `a` method
group.a.add(10);
assert_eq!((11,1),group.get_value());
// access to field `b` method
group.b.add(100);
assert_eq!((11,101),group.get_value());
}
Behind the scenes, the macro will generate some additional types
very similar to actor’s types, for group-actor
NameScriptGroup and NameLiveGroup:
Aa-AaScriptGroup,AaLiveGroupBb-BbScriptGroup,BbLiveGroup
For main-actor itself: NameGroupScript and NameGroupLive:
AaBb-AaBbGroupScript,AaBbGroupLive
In the context of the SDPL framework, group-actors are designated as SDL
(Script, Direct, Live) and will share the play method with the main-actor,
which is full SDPL.
The following is a type schema of the group model in relation
to the above example:
struct Aa;
struct Bb;
enum AaScriptGroup;
struct AaLiveGroup;
enum BbScriptGroup;
struct BbLiveGroup;
struct AaBb {
pub a: Aa,
pub b: Bb,
}
enum AaBbGroupScript;
struct AaBbGroupLive {
pub a: AaLiveGroup,
pub b: BbLiveGroup,
}
To view all the generated code by group, you can either utilize
the example macro or employ the
edit option within the group macro.
For a convenient shortcut to see the full example using edit,
simply use edit(file).“
#[interthread::group( file="path/to/file.rs",edit(file))]To inspect the generated code for field a type from the
above example, utilize the edit
option as edit(a::edit(file)), for struct AaBb itself
use edit(self::edit(file)).
§Requirements for Using the group Macro
Much like individual actors, the group macro enables a
set of actors to run collectively within a shared thread.
While many requirements align with those of individual actors,
there are some distinctions to be aware of. Below are the
crucial conditions that need to be satisfied for the group
macro to operate :
- The object must be a struct with named fields.
- As an
actorimpl block must contain a method namednewreturning a self-instance ortry_newif it may fail to return. - The macro requires a
filefield with a file path to the current file at all times. - Fields in the definition block that are intended to act as
group-actors should have non-private visibility (public or restricted). Private fields will not be considered asgroup-actorsby the macro.
§Configuration Options
The configuration options for a group are slightly different,
but consist of the same arguments as those used for an actor
except couple of them.
In some cases (see notation (AA) in the table below),
the argument is a list of the same arguments, specified as
argument(field_name::argument,..).
In context of the example code from above, if we wanted to
include any hypothetical static (associated) methods of struct Aa,
we would use the assoc argument, like so:
assoc(a::assoc)To include the same argument for main-actor itself, we would write
assoc(a::assoc, self::assoc)The following is the full table of configuration options:
#[interthread::group(
AA channel = 0 *
n (usize)
AA lib = "std" *
"smol"
"tokio"
"async_std"
AA file = "path/to/current/file.rs"
AA debut(
legend
)
(AA) assoc(
self::assoc,
..
)
(AA) edit(
self::edit(
script(..)
live(..)
),
..
)
(AA) name(
self::name = "",
..
)
(AA) path(
a::path = "path/to/type.rs",
..
)
)
]
* - default
AA - similar to `actor` attribute argument.
(AA) - a list of similar to `actor` attribute arguments.
All group configuration options (arguments) are the same as actor’s arguments,
except for path and allow, which are unique to group.
§Arguments
§path
Argument path is used when a group-actor is defined in a file different from the group itself.
§allow
Argument allow is used when a non-private field of the group is necessary but should not be included
as a group-actor.
§Handling Identical Types in the group Model
In certain situations, the group model may encounter a scenario where the main-actor
possesses multiple fields of the same type. Let’s consider an example:
struct AaBb {
pub a: Aa,
pub a1: Aa,
pub b: Bb,
}Due to the model naming convention which is based on type names, both fields a and a1 generate
identical model names for both the Script and Live components. This leads to a
compilation error:
the name `AaScriptGroup` is defined multiple times
`AaScriptGroup` must be defined only once in the type namespace of this module
To resolve this scenario, adjust the names for identical types as follows:
struct AaBb {
pub a: Aa,
pub a1: Aa,
pub b: Bb,
}
// Usage of the macro may be as follows:
#[interthread::group(
file="path/to/file.rs",
name( a1::name="Aa1" )
)]
impl AaBb {
// ...
}