-
Notifications
You must be signed in to change notification settings - Fork 24
Structs for Actions and Ports
This page describes some basic structs used in the C runtime for actions and ports. The diagrams below use the strategy of Inheritance in C.
The overall architecture is that each port and action has a custom struct type generated for it with a unique type name based on a hashcode (these unique codes are represented by NNNN
in the diagrams below).
When, within a reaction body, the code refers to an input or action by name, the variable's type is one of these custom types.
Because these types are available in the generated header files and visible within the reaction bodies, we call them here "user-facing structs."
They first few fields of the user-facing structs are common to all ports and actions; the last field is the value, and this field is specific to the particular port or action.
To access the common fields, a collection of non-user-facing struct types are defined that are guaranteed of having exactly the same layout as the user-facing structs for the fields they define. Hence, given a pointer to an instance of NNNN_portname_t
, the pointer can be cast to token_type_t*
, token_template_t*
, or lf_port_base_t*
.
Consider a reaction like this:
reaction(in) -> out {= ... =}
Within the body of the reaction, in
will have type idNNNN_in_t*
where id
is derived from the name of the reactor and NNNN
is a hashcode. The in
pointer will actually point to the output port of the reactor that is sending data to this reactor.
Thus, when a reaction reads in->value
, it is directly reading the value
field of the corresponding output port (unless the input port is declared mutable
, in which case this may point to a copy).
Each input port, action, and timer also has an instance of trigger_t
on the self struct of the parent reactor.
The trigger_t
struct contains the information needed to trigger reactions when an event is present on this trigger.
When a reaction body refers to a port or action by name, it sees structs whose type follow the following pattern:
classDiagram
class NNNN_portname_t {
token_type_t type
lf_token_t* token
size_t length
bool is_present
lf_port_internal_t _base
T value
}
NNNN_portname_t <|-- lf_port_internal_t
class lf_port_internal_t {
lf_sparse_io_record_t* sparse_record
int destination_channel
int num_destinations
self_base_t* source_reactor
}
class NNNN_actionname_t {
token_type_t type
lf_token_t* token
size_t length
bool is_present
lf_action_internal_t _base
self_base_t* parent
bool has_value
T value
}
NNNN_actionname_t <|-- lf_action_internal_t
class lf_action_internal_t {
trigger_t* trigger
}
NNNNN_self_t <|-- self_base_t
class NNNNN_self_t {
self_base_t base
instances of ports
instances of actions
instances of timers
}
The internal structs contain information that a reaction body will not normally use and cannot safely use (the data structure may change in the future).
The following structs are used internally in the C runtime and are required to have the same layout as the structs above:
classDiagram
class token_type_t {
size_t element_size
void (*destructor) (void* value)
void* (*copy_constructor) (void* value)
}
token_template_t <|-- token_type_t
class token_template_t {
token_type_t type
lf_token_t* token
size_t length
}
lf_port_base_t <|-- token_template_t
class lf_port_base_t {
token_template_t tmplt
bool is_present
lf_sparse_io_record_t* sparse_record
int destination_channel
int num_destinations
self_base_t* source_reactor
}
lf_action_base_t <|-- token_template_t
class lf_action_base_t {
token_template_t tmplt
bool is_present
trigger_t* trigger
self_base_t* parent
bool has_value
}
lf_action_base_t --> trigger_t
classDiagram
class self_base_t {
allocation_record_t* allocations
struct reaction_t* executing_reaction
void* reactor_mutex [if LF_THREADED]
reactor_mode_state_t _lf__mode_state [if MODAL_REACTORS]
}
NNNN_actionname_t --> self_base_t : parent
class lf_token_t {
void* value
size_t length
token_type_t* type
size_t ref_count
struct lf_token_t* next
}
NNNN_portname_t --> lf_token_t
NNNN_portname_t --> self_base_t : source_reactor
lf_port_base_t --> lf_token_t
lf_port_base_t --> self_base_t : source_reactor
NNNN_actionname_t --> lf_token_t
class trigger_t {
reaction_t** reactions
int number_of_reactions
bool is_timer
interval_t offset
interval_t period
bool is_physical
event_t* last
lf_spacing_policy_t policy
port_status_t status
reactor_mode_t* mode
tag_t last_known_status_tag [if FEDERATED]
bool is_a_control_reaction_waiting [if FEDERATED]
tag_t intended_tag [if FEDERATED]
instant_t physical_time_of_arrival [if FEDERATED]
}
lf_action_base_t --> trigger_t
lf_action_base_t --> self_base_t : parent
lf_action_base_t --> lf_token_t
NNNN_actionname_t --> trigger_t