reactor-c 1.0
C Runtime for Lingua Franca
Loading...
Searching...
No Matches
API for Reactions

API for use in inline reaction bodies. More...

Files

file  /Users/runner/work/reactor-c/reactor-c/logging/api/logging.h
 Logging API for the C target of Lingua Franca.
file  /Users/runner/work/reactor-c/reactor-c/include/core/port.h
 Support for optimizing sparse input through multiports.
file  /Users/runner/work/reactor-c/reactor-c/include/api/reaction_macros.h
 Macros providing an API for use in inline reaction bodies.
file  /Users/runner/work/reactor-c/reactor-c/include/core/reactor.h
 Definitions for the C target of Lingua Franca shared by threaded and unthreaded versions.
file  /Users/runner/work/reactor-c/reactor-c/include/api/schedule.h
 API functions for scheduling actions.
file  /Users/runner/work/reactor-c/reactor-c/tag/api/tag.h
 Time and tag definitions and functions for Lingua Franca.

Data Structures

struct  lf_multiport_iterator_t
 An iterator over a record of the subset of channels of a multiport that have present inputs. More...
struct  lf_token_t
 Token type for dynamically allocated arrays and structs sent as messages. More...
struct  tag_t
 A tag is a time, microstep pair. More...

Macros

#define CHECK_TIMEOUT(start, duration)
 Expression that is true if physical time since start exceeds the duration.
#define lf_add_worker_thread()
 Add a worker thread to the pool of worker threads of the environment invoking this reaction.
#define lf_added_worker_thread_count()
 Return the number of worker threads that have been added to the environment invoking this reaction.
#define LF_ASSERT(condition, format, ...)
 Assert that a condition is true.
#define LF_ASSERT_NON_NULL(pointer)
 Assert that a pointer is not NULL.
#define LF_ASSERTN(condition, format, ...)
 Assert that a condition is false.
#define lf_async(action, func, ...)
 Invoke a function that may block for a long time without blocking the rest of the program.
#define lf_await(port)
 Suspend this reaction until another reaction of this reactor calls lf_await_resume() on the specified input port.
#define lf_await_resume(port)
 Resume every reaction of this reactor that is suspended in lf_await() on the specified port.
#define lf_multiport_iterator(in)
 Macro for creating an iterator over an input multiport.
#define LF_PRINT_DEBUG(format, ...)
 A macro used to print useful debug information.
#define LF_PRINT_LOG(format, ...)
 A macro used to print useful logging information.
#define lf_reactor_full_name(reactor)
 Return the fully qualified name of the reactor.
#define lf_reactor_name(reactor)
 Return the instance name of the reactor.
#define lf_set(out, val)
 Set the specified output (or input of a contained reactor) to the specified value.
#define lf_set_array(out, val, len)
 Set the specified output (or input of a contained reactor) to the specified array with the given length.
#define lf_set_copy_constructor(out, cpy_ctor)
 Set the copy constructor associated with the specified port.
#define lf_set_destructor(out, dtor)
 Set the destructor associated with the specified port.
#define lf_set_mode(mode)
 Set the next mode of a modal reactor.
#define lf_set_present(out)
 Mark a port present.
#define lf_set_token(out, newtoken)
 Set the specified output (or input of a contained reactor) to the specified token value.
#define LF_SPARSE_CAPACITY_DIVIDER   10
 Divide LF_SPARSE_WIDTH_THRESHOLD by this number to get the capacity of a sparse input record for a multiport.
#define LF_SPARSE_WIDTH_THRESHOLD   10
 Threshold for width of multiport s.t.
#define lf_tag()
 Return the current tag of the environment invoking this reaction.
#define LF_TEST(condition, format, ...)
 Check that a condition is true.
#define LF_TIME_BUFFER_LENGTH   64
 For user-friendly reporting of time values, the buffer length required.
#define lf_time_logical()
 Return the current logical time in nanoseconds of the environment invoking this reaction.
#define lf_time_logical_elapsed()
 Return the current logical time of the environment invoking this reaction relative to the start time in nanoseconds.
#define LOG_LEVEL_ALL   255
 All log levels.
#define LOG_LEVEL_DEBUG   4
 Debug log level.
#define LOG_LEVEL_ERROR   0
 Error log level, which is the lowest log level.
#define LOG_LEVEL_INFO   2
 Warning log level.
#define LOG_LEVEL_LOG   3
 Log log level.
#define LOG_LEVEL_WARNING   1
 Warning log level.

Typedefs

typedef int64_t instant_t
 Time instant.
typedef int64_t interval_t
 Interval of time.
typedef struct lf_multiport_iterator_t lf_multiport_iterator_t
 An iterator over a record of the subset of channels of a multiport that have present inputs.
typedef struct lf_token_t lf_token_t
 Token type for dynamically allocated arrays and structs sent as messages.
typedef uint32_t microstep_t
 Microstep.
typedef void print_message_function_t(const char *, va_list)
 Message print function type.
typedef char * string
 String type so that we don't have to use {= char* =}.

Functions

int lf_add_worker_thread (environment_t *env)
 Add a worker thread to the pool of worker threads of the specified environment.
int lf_added_worker_thread_count (environment_t *env)
 Return the number of worker threads added to the specified environment.
void lf_async_resume (void *action)
 Resume a reaction suspended in lf_async() that is waiting on the specified action.
bool lf_check_deadline (void *self, bool invoke_deadline_handler)
 Check the deadline of the currently executing reaction against the current physical time.
size_t lf_comma_separated_time (char *buffer, instant_t time)
 Print a non-negative time value in nanoseconds with commas separating thousands into the specified buffer.
tag_t lf_delay_tag (tag_t tag, interval_t interval)
 Delay a tag by the specified time interval to realize the "after" keyword.
bool lf_is_tag_after_stop_tag (environment_t *env, tag_t tag)
 Return true if the provided tag is after stop tag.
int lf_multiport_next (lf_multiport_iterator_t *iterator)
 Return the channel number of the next present input on the multiport or -1 if there are no more present channels.
lf_token_t * lf_new_token (void *port_or_action, void *val, size_t len)
 Return a new disassociated token with type matching the specified port or action and containing the specified value and length.
void lf_print (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report an informational message on stdout with a newline appended at the end.
void void void void lf_print_debug (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report an debug message on stdout with the prefix "DEBUG: " and a newline appended at the end.
void void void void void lf_print_error (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report an error with the prefix "ERROR: " and a newline appended at the end.
void void void void void void void lf_print_error_and_exit (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report an error with the prefix "ERROR: " and a newline appended at the end, then exit with the failure code EXIT_FAILURE.
void void void void void void void void lf_print_error_system_failure (const char *format,...)
 Report an error and exit just like lf_print_error_and_exit(), but also print the system error message associated with the error.
void void lf_print_info (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report an informational message on stdout with no prefix and a newline appended at the end.
void void void lf_print_log (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report an log message on stdout with the prefix "LOG: " and a newline appended at the end.
void void void void void void lf_print_warning (const char *format,...) ATTRIBUTE_FORMAT_PRINTF(1
 Report a warning with the prefix "WARNING: " and a newline appended at the end.
const char * lf_reactor_full_name (self_base_t *self)
 Return the full name of the reactor.
const char * lf_reactor_name (self_base_t *self)
 Return the instance name of the reactor.
size_t lf_readable_time (char *buffer, instant_t time)
 Store into the specified buffer a string giving a human-readable rendition of the specified time.
void lf_register_print_function (print_message_function_t *function, int log_level)
 Register a function to display messages.
void lf_request_stop (void)
 Request a stop to execution as soon as possible.
trigger_handle_t lf_schedule (void *action, interval_t offset)
 Schedule an action to occur with the specified time offset with no payload (no value conveyed).
trigger_handle_t lf_schedule_copy (void *action, interval_t offset, void *value, size_t length)
 Schedule an action to occur with the specified value and time offset with a copy of the specified value.
trigger_handle_t lf_schedule_int (void *action, interval_t extra_delay, int value)
 Schedule the specified action with an integer value at a later logical time.
trigger_handle_t lf_schedule_token (void *action, interval_t extra_delay, lf_token_t *token)
 Schedule the specified action at a later tag with the specified token as a payload.
trigger_handle_t lf_schedule_value (void *action, interval_t extra_delay, void *value, int length)
 Variant of lf_schedule_token that creates a token to carry the specified value.
void lf_set_present (lf_port_base_t *port)
 Mark the given port's is_present field as true.
tag_t lf_tag (void *env)
 Return the current tag, a logical time, microstep pair.
tag_t lf_tag_add (tag_t a, tag_t b)
 Add two tags.
int lf_tag_compare (tag_t tag1, tag_t tag2)
 Compare two tags.
tag_t lf_tag_max (tag_t tag1, tag_t tag2)
 Return the greater out of two tags.
tag_t lf_tag_min (tag_t tag1, tag_t tag2)
 Return the lesser out of two tags.
instant_t lf_time_add (instant_t a, interval_t b)
 Return the sum of an interval and an instant, saturating on overflow and underflow.
int lf_time_parse (const char *time_str, const char *units_str, interval_t *result)
 Parse a time value and units from strings, producing a time in nanoseconds.
instant_t lf_time_physical (void)
 Return the current physical time in nanoseconds.
instant_t lf_time_physical_elapsed (void)
 Return the elapsed physical time in nanoseconds.
instant_t lf_time_start (void)
 Return the physical and logical time of the start of execution in nanoseconds.
instant_t lf_time_subtract (instant_t a, interval_t b)
 Return an instant minus an interval, saturating on overflow and underflow.
void lf_update_deadline (void *self, interval_t updated_deadline)
 Update the deadline of the currently executing reaction.
void lf_watchdog_start (watchdog_t *watchdog, interval_t additional_timeout)
 Start or restart the watchdog timer.
void lf_watchdog_stop (watchdog_t *watchdog)
 Stop the specified watchdog without invoking the expiration handler.
lf_token_t * lf_writable_copy (lf_port_base_t *port)
 Return a writable copy of the token in the specified template.
int register_user_trace_event (void *self, char *description)
 Register a user trace event.
void tracepoint_user_event (void *self, char *description)
 Trace a user-defined event.
void tracepoint_user_value (void *self, char *description, long long value)
 Trace a user-defined event with a value.

Detailed Description

API for use in inline reaction bodies.

These macros and functions are automatically available in any reaction body that is inlined inside the .lf file; there is no need to add any #include.

Note
For reactions that are defined in ordinary C files instead of inline in the .lf file, (see Reaction Declarations), you may need to use a function directly instead of a macro. The corresponding very likely takes an environment argument. The self struct struct has an environment field, so you can pass as the argument self->environment.

Macro Definition Documentation

◆ CHECK_TIMEOUT

#define CHECK_TIMEOUT ( start,
duration )

#include <tag.h>

Value:
(lf_time_physical() > ((start) + (duration)))
instant_t lf_time_physical(void)
Return the current physical time in nanoseconds.
instant_t duration

Expression that is true if physical time since start exceeds the duration.

Parameters
startThe start time.
durationThe duration.

◆ lf_add_worker_thread

#define lf_add_worker_thread ( )

#include <reaction_macros.h>

Value:
lf_add_worker_thread(self->base.environment)
#define lf_add_worker_thread()
Add a worker thread to the pool of worker threads of the environment invoking this reaction.
Definition reaction_macros.h:220

Add a worker thread to the pool of worker threads of the environment invoking this reaction.

See lf_add_worker_thread(environment_t*) for details.

Returns
0 on success, or -1 if the thread could not be created or the runtime does not support it.

◆ lf_added_worker_thread_count

#define lf_added_worker_thread_count ( )

#include <reaction_macros.h>

Value:
lf_added_worker_thread_count(self->base.environment)
#define lf_added_worker_thread_count()
Return the number of worker threads that have been added to the environment invoking this reaction.
Definition reaction_macros.h:228

Return the number of worker threads that have been added to the environment invoking this reaction.

See lf_added_worker_thread_count(environment_t*) for details.

◆ LF_ASSERT

#define LF_ASSERT ( condition,
format,
... )

#include <logging_macros.h>

Value:
do { \
if (!(condition)) { \
lf_print_error_and_exit("`" format "`. Failed assertion in %s:%d(%s):(" #condition ") != true`", ##__VA_ARGS__, \
__FILE__, __LINE__, __func__); \
} \
} while (0)

Assert that a condition is true.

This will verify that the condition is true and call lf_print_error_and_exit if it is not true. The remaining arguments are passed to lf_print_error_and_exit as the format string and arguments.

This is optimized to execute the condition argument but not check the result if the NDEBUG flag is defined. The NDEBUG flag will be defined if the user specifies build-type: Release in the target properties of the LF program.

Parameters
conditionThe condition to verify.
formatThe format string to pass to lf_print_error_and_exit.
...The arguments to pass to lf_print_error_and_exit.

◆ LF_ASSERT_NON_NULL

#define LF_ASSERT_NON_NULL ( pointer)

#include <logging_macros.h>

Value:
do { \
if (!(pointer)) { \
lf_print_error_and_exit("`Out of memory?` Assertion failed in %s:%d(%s):`" #pointer " == NULL`", __FILE__, \
__LINE__, __func__); \
} \
} while (0)

Assert that a pointer is not NULL.

This will verify that the pointer is non-NULL and call lf_print_error_and_exit if it is NULL.

This differs from LF_ASSERT in that it does nothing at all if the NDEBUG flag is defined.

Parameters
pointerThe pointer to verify.

◆ LF_ASSERTN

#define LF_ASSERTN ( condition,
format,
... )

#include <logging_macros.h>

Value:
do { \
if (condition) { \
lf_print_error_and_exit("`" format "`. Failed assertion in %s:%d(%s):(" #condition ") != false`", ##__VA_ARGS__, \
__FILE__, __LINE__, __func__); \
} \
} while (0)

Assert that a condition is false.

This will verify that the condition is false and call lf_print_error_and_exit if it is not false. The remaining arguments are passed to lf_print_error_and_exit as the format string and arguments.

This is optimized to execute the condition argument but not check the result if the NDEBUG flag is defined. The NDEBUG flag will be defined if the user specifies build-type: Release in the target properties of the LF program.

Parameters
conditionThe condition to verify.
formatThe format string to pass to lf_print_error_and_exit.
...The arguments to pass to lf_print_error_and_exit.

◆ lf_async

#define lf_async ( action,
func,
... )

#include <reaction_macros.h>

Value:
({ \
lf_async_state_t _lf_async_state; \
_lf_async_begin(&_lf_async_state, self, (action), NULL); \
__typeof__(func(__VA_ARGS__)) _lf_async_result = func(__VA_ARGS__); \
_lf_async_end(&_lf_async_state); \
_lf_async_result; \
})
State of a pending lf_async() call.
Definition reactor.h:203

Invoke a function that may block for a long time without blocking the rest of the program.

This macro invokes func with the given arguments and evaluates to the value returned by func, but it allows logical time to advance and other reactions to execute while func is running. The calling reaction must list action as an effect, so that the action is in scope, and the reactor must have a reaction triggered by that action whose body calls lf_async_resume(). That resuming reaction must be declared before the calling reaction:

physical action awake
reaction(awake) {= lf_async_resume(awake); =}
reaction(startup) -> awake {= int result = lf_async(awake, slow, 50); ... =}
#define lf_async(action, func,...)
Invoke a function that may block for a long time without blocking the rest of the program.
Definition reaction_macros.h:302
void lf_async_resume(void *action)
Resume a reaction suspended in lf_async() that is waiting on the specified action.

A physical action is dedicated to the reaction that calls lf_async() on it. The same reaction may call it more than once, and a call from any other reaction is a fatal error. The action may instead be a logical action. A logical action is scheduled before func is invoked, at the current tag plus the action's minimum delay (or, if no minimum delay is given, one microstep later than the current tag). A physical action is scheduled when func returns, at the physical time then. Hence, the logical time after the lf_async() call will be determined by the physical time when func returns.

Specifically:

  1. The func is invoked with the given arguments, and its return value becomes the return value of the lf_async() call.
  2. Any outputs set before a call to lf_async() are produced at the current tag. During the call to lf_async(), the tag advances. Any outputs set after the call will be produced at the new tag.
  3. If action is logical, when lf_async() returns, the tag will be at least one microstep later than the current tag before the call to lf_async(). If a min_delay is given for the logical action, then logical time will be at least that delay later. Outputs set after the call to lf_async() are produced at the new tag.
  4. If action is physical, it is scheduled when func returns. The calling thread then blocks until the reaction triggered by action calls lf_async_resume(action). If action is logical and its delay elapses before func returns, the triggered reaction calls lf_async_resume() first and blocks until func returns. The worker executing it stays busy, so logical time does not advance past that tag. The calling reaction then continues when the scheduler reaches that reaction's level, and a higher level does not start until the continuation has completed. Several reactions may suspend on one logical action; each continues at its own level. A second call for the same logical action and tag does not schedule another event.
  5. To ensure that other reactions can execute while func is running, if needed (see the caveats below), a worker thread is automatically added to the pool of the environment (see lf_add_worker_thread()). Then the worker thread executing the reaction that calls lf_async() temporarily leaves the pool. The remaining workers may therefore advance logical time and execute other reactions as if the calling reaction had completed.
  6. When lf_async() returns, the calling thread rejoins the pool.

Caveats:

  • func must return a value; functions returning void are not supported.
  • The value of state variables is not preserved across lf_async() calls. Other reactions in this reactor may have modified them before lf_async() returns.
  • The calling reaction counts as executing while it is suspended. If it is triggered again at an intervening tag, that triggering is dropped. Other reactions of the same reactor can execute at intervening tags, so state shared with them should be handled with care.
  • The reactions of the reactor are mutually exclusive. The first call creates a reactor mutex if the reactor does not already have one. This is similar to reactors with watchdogs.
  • At least one worker thread is added to the pool, and it persists until the program terminates. Additional worker thread will be added only if there are multiple simultaneous calls to lf_async() pending completion.
  • If the program stops before the resuming reaction runs, the call returns at the stop tag after the shutdown reactions have executed. Any further lf_async() call made by the resumed reaction invokes func directly without suspending.
  • This requires the threaded runtime with the NP (default) or GEDF_NP scheduler, and a compiler that supports statement expressions and __typeof__ (GCC and Clang do).
Parameters
actionThe logical or physical action that resumes this call, as it is in scope in the calling reaction. The reaction must list it as an effect.
funcThe function to invoke.
...The arguments to pass to func.
Returns
The value returned by func.

◆ lf_await

#define lf_await ( port)

#include <reaction_macros.h>

Value:
_lf_await(self, (port))
void _lf_await(void *self, void *port)
Suspend the calling reaction until another reaction of the same reactor calls lf_await_resume() on th...

Suspend this reaction until another reaction of this reactor calls lf_await_resume() on the specified input port.

Like lf_async(), this allows logical time to advance and other reactions to execute while the calling reaction is suspended, and the remainder of the reaction executes at the tag at which lf_await_resume(port) is called. Unlike lf_async(), no function is invoked and no action is needed. The calling reaction must list port as a source so that it is in scope, and the resuming reaction must be declared before the calling reaction. For example, a reaction that sends a request and waits for the response to arrive on an input:

reaction(response) {= lf_await_resume(response); =}
reaction(startup) response -> request {=
lf_set(request, 42);
lf_await(response);
// Now at the tag at which the response arrived, and response->value is available.
=}
#define lf_await(port)
Suspend this reaction until another reaction of this reactor calls lf_await_resume() on the specified...
Definition reaction_macros.h:337
#define lf_await_resume(port)
Resume every reaction of this reactor that is suspended in lf_await() on the specified port.
Definition reaction_macros.h:347
#define lf_set(out, val)
Set the specified output (or input of a contained reactor) to the specified value.
Definition reaction_macros.h:73

Several reactions of the reactor may be suspended on the same port at once; a single lf_await_resume(port) resumes all of them. If the program stops before lf_await_resume() is called, lf_await() returns at the stop tag so that the reaction can complete. See _lf_await() for details, and see lf_async() for the caveats, which also apply.

Parameters
portAn input port of this reactor, as it is in scope in the reaction body.

◆ lf_await_resume

#define lf_await_resume ( port)

#include <reaction_macros.h>

Value:
_lf_await_resume(self, (port))
void _lf_await_resume(void *self, void *port)
Resume every reaction of this reactor that is suspended in lf_await() on the specified port.

Resume every reaction of this reactor that is suspended in lf_await() on the specified port.

If no reaction is suspended on the port, this does nothing. See _lf_await_resume() for details.

Parameters
portThe input port, as it is in scope in the reaction body.

◆ lf_multiport_iterator

#define lf_multiport_iterator ( in)

#include <port.h>

Value:
(_lf_multiport_iterator_impl((lf_port_base_t**)self->_lf_##in, self->_lf_##in##_width))
lf_multiport_iterator_t _lf_multiport_iterator_impl(lf_port_base_t **port, int width)
Given an array of pointers to port structs, return an iterator that can be used to iterate over the p...
Base type for ports.
Definition lf_token.h:202

Macro for creating an iterator over an input multiport.

The argument is the port name. This returns an instance of lf_multiport_iterator_t on the stack, a pointer to which should be passed to lf_multiport_next() to advance.

Parameters
inThe port name.

◆ LF_PRINT_DEBUG

#define LF_PRINT_DEBUG ( format,
... )

#include <logging_macros.h>

Value:
do { \
lf_print_debug(format, ##__VA_ARGS__); \
} \
} while (0)
static const bool _lf_log_level_is_debug
Definition logging_macros.h:24

A macro used to print useful debug information.

In contrast to the function lf_print_debug, this macro avoids the overhead if logging is disabled.

It can be enabled by setting the target property 'logging' to 'DEBUG' or by defining LOG_LEVEL to LOG_LEVEL_DEBUG in the top-level preamble. The input to this macro is exactly like printf: (format, ...). "DEBUG: " is prepended to the beginning of the message and a newline is appended to the end of the message.

Note
This macro is non-empty even if LOG_LEVEL is not defined in user-code. This is to ensure that the compiler will still parse the predicate inside (...) to prevent LF_PRINT_DEBUG statements to fall out of sync with the rest of the code. This should have a negligible impact on performance if compiler optimization (e.g., -O2 for gcc) is used as long as the arguments passed to it do not themselves incur significant overhead to evaluate.

◆ LF_PRINT_LOG

#define LF_PRINT_LOG ( format,
... )

#include <logging_macros.h>

Value:
do { \
lf_print_log(format, ##__VA_ARGS__); \
} \
} while (0)
static const bool _lf_log_level_is_log
Definition logging_macros.h:23

A macro used to print useful logging information.

In contrast to the function lf_print_log, this macro avoids the overhead if logging is disabled.

It can be enabled by setting the target property 'logging' to 'LOG' or by defining LOG_LEVEL to LOG_LEVEL_LOG or LOG_LEVEL_DEBUG in the top-level preamble. The input to this macro is exactly like printf: (format, ...). "LOG: " is prepended to the beginning of the message and a newline is appended to the end of the message.

Note
This macro is non-empty even if LOG_LEVEL is not defined in user-code. This is to ensure that the compiler will still parse the predicate inside (...) to prevent LF_PRINT_LOG statements to fall out of sync with the rest of the code. This should have a negligible impact on performance if compiler optimization (e.g., -O2 for gcc) is used as long as the arguments passed to it do not themselves incur significant overhead to evaluate.

◆ lf_reactor_full_name

#define lf_reactor_full_name ( reactor)

#include <reaction_macros.h>

Value:
lf_reactor_full_name(&reactor->base)
#define lf_reactor_full_name(reactor)
Return the fully qualified name of the reactor.
Definition reaction_macros.h:370

Return the fully qualified name of the reactor.

The fully qualified name of a reactor is the instance name of the reactor concatenated with the names of all of its parents, separated by dots. If the reactor or any of its parents is a bank, then the name will have a suffix of the form [bank_index].

Parameters
reactorThe reactor to get the name of.

◆ lf_reactor_name

#define lf_reactor_name ( reactor)

#include <reaction_macros.h>

Value:
lf_reactor_name(&reactor->base)
#define lf_reactor_name(reactor)
Return the instance name of the reactor.
Definition reaction_macros.h:358

Return the instance name of the reactor.

The instance name is the name of given to the instance created by the new operator in LF. If the instance is in a bank, then the name will have a suffix of the form [bank_index].

Parameters
reactorThe reactor to get the name of.

◆ lf_set

#define lf_set ( out,
val )

#include <reaction_macros.h>

Value:
do { \
out->value = val; \
lf_set_present(out); \
if (((token_template_t*)out)->token != NULL) { \
\
\
lf_token_t* token = _lf_initialize_token_with_value((token_template_t*)out, *((void**)&out->value), 1); \
out->token = token; \
} \
} while (0)
lf_token_t * _lf_initialize_token_with_value(token_template_t *tmplt, void *value, size_t length)
Return a token storing the specified value, which is assumed to be either a scalar (if length is 1) o...
Token type for dynamically allocated arrays and structs sent as messages.
Definition lf_token.h:99
Base type for ports (lf_port_base_t) and actions (trigger_t), which can carry tokens.
Definition lf_token.h:156
lf_token_t * token
Pointer to the current token.
Definition lf_token.h:174

Set the specified output (or input of a contained reactor) to the specified value.

If the value argument is a primitive type such as int, double, etc. as well as the built-in types bool and string, the value is copied and therefore the variable carrying the value can be subsequently modified without changing the output. This also applies to structs with a type defined by a typedef so that the type designating string does not end in '*'.

If the value argument is a pointer to memory that the calling reaction has dynamically allocated, the memory will be automatically freed once all downstream reactions no longer need the value. If 'lf_set_destructor' is called on 'out', then that destructor will be used to free 'value'. Otherwise, the default void free(void*) function is used.

Parameters
outThe output port (by name) or input of a contained reactor in form input_name.port_name.
valThe value to insert into the self struct.

◆ lf_set_array

#define lf_set_array ( out,
val,
len )

#include <reaction_macros.h>

Value:
do { \
lf_set_present(out); \
out->token = token; \
out->value = token->value; \
out->length = len; \
} while (0)
void * value
Pointer to dynamically allocated memory containing a message.
Definition lf_token.h:101

Set the specified output (or input of a contained reactor) to the specified array with the given length.

The array is assumed to be in dynamically allocated memory. The deallocation is delegated to downstream reactors, which automatically deallocate when the reference count drops to zero.

Parameters
outThe output port (by name).
valThe array to send (a pointer to the first element).
lenThe length of the array to send.

◆ lf_set_copy_constructor

#define lf_set_copy_constructor ( out,
cpy_ctor )

#include <reaction_macros.h>

Value:
((token_type_t*)out)->copy_constructor = cpy_ctor
Type information for tokens.
Definition lf_token.h:71

Set the copy constructor associated with the specified port.

The copy constructor will be used to copy any value sent through the specified port whenever a downstream user of the value declares a mutable input port or calls lf_writable_copy().

Parameters
outThe output port (by name) or input of a contained reactor in form reactor.port_name.
cpy_ctorA pointer to a void function that takes a pointer argument (or NULL to use the default void memcpy() function.

◆ lf_set_destructor

#define lf_set_destructor ( out,
dtor )

#include <reaction_macros.h>

Value:
((token_type_t*)out)->destructor = dtor

Set the destructor associated with the specified port.

The destructor will be used to free any value sent through the specified port when all downstream users of the value are finished with it.

Parameters
outThe output port (by name) or input of a contained reactor in form reactor.port_name.
dtorA pointer to a void function that takes a pointer argument (or NULL to use the default void free(void*) function.

◆ lf_set_mode

#define lf_set_mode ( mode)

#include <reaction_macros.h>

Value:
_LF_SET_MODE_WITH_TYPE(mode, _lf_##mode##_change_type)
#define _LF_SET_MODE_WITH_TYPE(mode, change_type)
Set the next mode of a modal reactor with an explicit change type (reset or history,...
Definition modes.h:50

Set the next mode of a modal reactor.

As with lf_set for outputs, only the last value will have effect if invoked multiple times at any given tag. This works only in reactions with the target mode declared as effect.

Parameters
modeThe target mode to set for activation.

◆ lf_set_present

#define lf_set_present ( out)

#include <reaction_macros.h>

Value:
#define lf_set_present(out)
Mark a port present.
Definition reaction_macros.h:48

Mark a port present.

This sets the is_present field of the specified output to true.

This macro is a thin wrapper around the lf_set_present() function. It simply casts the argument to lf_port_base_t* to suppress warnings.

Parameters
outThe output port (by name).

◆ lf_set_token

#define lf_set_token ( out,
newtoken )

#include <reaction_macros.h>

Value:
do { \
lf_set_present(out); \
_lf_replace_template_token((token_template_t*)out, newtoken); \
out->value = newtoken->value; \
out->length = newtoken->length; \
} while (0)

Set the specified output (or input of a contained reactor) to the specified token value.

Tokens in the C runtime wrap messages that are in dynamically allocated memory and perform reference counting to ensure that memory is not freed prematurely.

Parameters
outThe output port (by name).
newtokenA pointer to token obtained from an input, an action, or from lf_new_token().

◆ LF_SPARSE_CAPACITY_DIVIDER

#define LF_SPARSE_CAPACITY_DIVIDER   10

#include <port.h>

Divide LF_SPARSE_WIDTH_THRESHOLD by this number to get the capacity of a sparse input record for a multiport.

◆ LF_SPARSE_WIDTH_THRESHOLD

#define LF_SPARSE_WIDTH_THRESHOLD   10

#include <port.h>

Threshold for width of multiport s.t.

sparse reading is supported.

◆ lf_tag

#define lf_tag ( )

#include <reaction_macros.h>

Value:
lf_tag(self->base.environment)
#define lf_tag()
Return the current tag of the environment invoking this reaction.
Definition reaction_macros.h:198

Return the current tag of the environment invoking this reaction.

◆ LF_TEST

#define LF_TEST ( condition,
format,
... )

#include <logging_macros.h>

Value:
do { \
if (!(condition)) { \
lf_print_error_and_exit("`" format "`. Failed assertion in %s:%d(%s):(" #condition ") != true`", ##__VA_ARGS__, \
__FILE__, __LINE__, __func__); \
} \
} while (0)

Check that a condition is true.

This will verify that the condition is true and call lf_print_error_and_exit if it is not true. The remaining arguments are passed to lf_print_error_and_exit as the format string and arguments. This is just like LF_ASSERT, except that it is not optimized away when the NDEBUG flag is defined.

Parameters
conditionThe condition to verify.
formatThe format string to pass to lf_print_error_and_exit.
...The arguments to pass to lf_print_error_and_exit.

◆ LF_TIME_BUFFER_LENGTH

#define LF_TIME_BUFFER_LENGTH   64

#include <tag.h>

For user-friendly reporting of time values, the buffer length required.

This is calculated as follows, based on 64-bit time in nanoseconds: Maximum number of weeks is 15,250 Maximum number of days is 6 Maximum number of hours is 23 Maximum number of minutes is 59 Maximum number of seconds is 59 Maximum number of nanoseconds is 999,999,999 Maximum number of microsteps is 4,294,967,295 Total number of characters for the above is 24. Text descriptions and spaces add an additional 30, for a total of 54. One more allows for a null terminator. Round up to a power of two.

◆ lf_time_logical

#define lf_time_logical ( )

#include <reaction_macros.h>

Value:
lf_time_logical(self->base.environment)
#define lf_time_logical()
Return the current logical time in nanoseconds of the environment invoking this reaction.
Definition reaction_macros.h:204

Return the current logical time in nanoseconds of the environment invoking this reaction.

◆ lf_time_logical_elapsed

#define lf_time_logical_elapsed ( )

#include <reaction_macros.h>

Value:
lf_time_logical_elapsed(self->base.environment)
#define lf_time_logical_elapsed()
Return the current logical time of the environment invoking this reaction relative to the start time ...
Definition reaction_macros.h:211

Return the current logical time of the environment invoking this reaction relative to the start time in nanoseconds.

◆ LOG_LEVEL_ALL

#define LOG_LEVEL_ALL   255

#include <logging.h>

All log levels.

See also
LOG_LEVEL_ERROR

◆ LOG_LEVEL_DEBUG

#define LOG_LEVEL_DEBUG   4

#include <logging.h>

Debug log level.

See also
LOG_LEVEL_ERROR

◆ LOG_LEVEL_ERROR

#define LOG_LEVEL_ERROR   0

#include <logging.h>

Error log level, which is the lowest log level.

This is the lowest log level.

Note
LOG_LEVEL is set in generated code to 0 through 4 if the target logging property is error, warning, info, log, or debug. The default level is info (2). Currently, 0, 1, and 2 are treated identically and lf_print_error, lf_print_warning, and lf_print LOG_LEVEL is set in generated code to 0 through 4 if the target logging property is error, warning, info, log, or debug. The default level is info (2). Currently, 0, 1, and 2 are treated identically and lf_print_error, lf_print_warning, and lf_print all result in printed output. If log is set (3), then lf_print_log messages will be printed as well. If debug is set (4), the lf_print_debug messages will be printed as well.

◆ LOG_LEVEL_INFO

#define LOG_LEVEL_INFO   2

#include <logging.h>

Warning log level.

See also
LOG_LEVEL_ERROR

◆ LOG_LEVEL_LOG

#define LOG_LEVEL_LOG   3

#include <logging.h>

Log log level.

See also
LOG_LEVEL_ERROR

◆ LOG_LEVEL_WARNING

#define LOG_LEVEL_WARNING   1

#include <logging.h>

Warning log level.

See also
LOG_LEVEL_ERROR

Typedef Documentation

◆ instant_t

typedef int64_t instant_t

#include <tag.h>

Time instant.

Both physical and logical times are represented using this typedef.

◆ interval_t

typedef int64_t interval_t

#include <tag.h>

Interval of time.

◆ lf_multiport_iterator_t

typedef struct lf_multiport_iterator_t lf_multiport_iterator_t

#include <port.h>

An iterator over a record of the subset of channels of a multiport that have present inputs.

To use this, create an iterator using the function lf_multiport_iterator(). That function returns a struct that can be passed to the lf_multiport_next() function to obtain the next channel number of a present input (or -1 if there is no next present input).

◆ lf_token_t

typedef struct lf_token_t lf_token_t

#include <lf_token.h>

Token type for dynamically allocated arrays and structs sent as messages.

This struct is the wrapper around the dynamically allocated memory that carries the message. The message can be an array of values, where the size of each value is element_size (in bytes). If it is not an array, or is not to be treated as an array, the length == 1.

In the C LF target, a type for an output that ends in '' or '[]' is treated specially. The value carried by the output is assumed to be in dynamically allocated memory, and, using reference counting, after the last downstream reader of the value has finished, the memory will be freed. To prevent this freeing from occurring, the output type can be specified using the syntax {= type =}; this will not be treated as dynamically allocated memory. Alternatively, the programmer can give a typedef in the preamble that masks the trailing *.

◆ microstep_t

typedef uint32_t microstep_t

#include <tag.h>

Microstep.

◆ print_message_function_t

typedef void print_message_function_t(const char *, va_list)

#include <logging.h>

Message print function type.

The arguments passed to one of these print functions are a printf-style format strin followed by a printf-style argument list collected into a va_list (variable argument list).

◆ string

typedef char* string

#include <lf_types.h>

String type so that we don't have to use {= char* =}.

Use this as the type for ports sending and receiving strings that are not dynamically allocated. For dynamically allocated strings that have to be freed after being consumed downstream, use type char*.

Function Documentation

◆ lf_add_worker_thread()

int lf_add_worker_thread ( environment_t * env)

#include <reactor.h>

Add a worker thread to the pool of worker threads of the specified environment.

The new thread joins the pool immediately and starts executing reactions alongside the existing workers. This can be used, for example, to compensate for a worker thread that is about to block for an extended time inside a reaction. The thread remains in the pool until the program terminates, when it is joined like any other worker thread.

This function must be called from within a reaction body, i.e., from one of the worker threads of the environment. In a reaction body, the environment is self->base.environment, and the convenience macro lf_add_worker_thread() (with no arguments) supplies it.

Adding worker threads is supported by the default (NP) and GEDF_NP schedulers in the threaded runtime. The adaptive scheduler and the single-threaded runtime do not support it, in which case this function returns -1 and no thread is created.

Parameters
envThe environment to which to add a worker thread.
Returns
0 on success, or -1 if the thread could not be created or the runtime does not support adding worker threads.

◆ lf_added_worker_thread_count()

int lf_added_worker_thread_count ( environment_t * env)

#include <reactor.h>

Return the number of worker threads added to the specified environment.

This counts the worker threads that have been successfully added by calls to lf_add_worker_thread(). It does not include the worker threads created at startup. In a reaction body, the environment is self->base.environment, and the convenience macro lf_added_worker_thread_count() (with no arguments) supplies it.

Parameters
envThe environment.
Returns
The number of worker threads added at runtime (0 in the single-threaded runtime).

◆ lf_async_resume()

void lf_async_resume ( void * action)

#include <reactor.h>

Resume a reaction suspended in lf_async() that is waiting on the specified action.

This must be called from the body of a reaction triggered by the action passed to lf_async(), and this reaction must belong to the same reactor as the action. For example:

reaction(awake) {= lf_async_resume(awake); =}

Every pending call on this action whose scheduled tag has been reached is resumed, oldest first. A physical action cannot be shared with another reaction, so it has at most one such call. If a call's function has not yet returned, this function blocks until _lf_async_end() is called for it before considering the next call. The calling worker is busy while it blocks, so logical time cannot advance past this tag. A suspended reaction does not continue inline: it continues when the scheduler reaches that reaction's level, and the scheduler does not start a higher level until the continuation has completed. Reactions of one reactor have distinct levels, so the continuations run in declaration order, before any later reaction triggered by the action. If no due call is waiting on this action, this function does nothing. Only this reaction incurs any cost; the runtime does not check for pending lf_async() calls anywhere else.

Parameters
actionThe action, as it is in scope in the reaction body.

◆ lf_check_deadline()

bool lf_check_deadline ( void * self,
bool invoke_deadline_handler )

#include <schedule.h>

Check the deadline of the currently executing reaction against the current physical time.

If the deadline has passed, invoke the deadline handler (if invoke_deadline_handler parameter is set true) and return true. Otherwise, return false.

This function is intended to be used within a reaction that has been invoked without a deadline violation, but that wishes to check whether the deadline gets violated during the execution of the reaction. This can be used, for example, to implement a timeout mechanism that bounds the execution time of a reaction, for example to realize an "anytime" computation.

Parameters
selfThe self struct of the reactor.
invoke_deadline_handlerWhen this is set true, also invoke deadline handler if the deadline has passed.
Returns
True if the specified deadline has passed and false otherwise.

◆ lf_comma_separated_time()

size_t lf_comma_separated_time ( char * buffer,
instant_t time )

#include <tag.h>

Print a non-negative time value in nanoseconds with commas separating thousands into the specified buffer.

Ideally, this would use the locale to use periods if appropriate, but I haven't found a sufficiently portable way to do that.

Parameters
bufferA buffer long enough to contain a string like "9,223,372,036,854,775,807".
timeA time value.
Returns
The number of characters written (not counting the null terminator).

◆ lf_delay_tag()

tag_t lf_delay_tag ( tag_t tag,
interval_t interval )

#include <tag.h>

Delay a tag by the specified time interval to realize the "after" keyword.

Any interval less than 0 (including NEVER) is interpreted as "no delay", whereas an interval equal to 0 is interpreted as one microstep delay. If the time field of the tag is NEVER or the interval is negative, return the unmodified tag. If the time interval is 0LL, add one to the microstep, leave the time field alone, and return the result. Otherwise, add the interval to the time field of the tag and reset the microstep to 0. If the sum overflows, saturate the time value at FOREVER. For example:

  • if tag = (t, 0) and interval = 10, return (t + 10, 0)
  • if tag = (t, 0) and interval = 0, return (t, 1)
  • if tag = (t, 0) and interval = NEVER, return (t, 0)
  • if tag = (FOREVER, 0) and interval = 10, return (FOREVER, 0)
Parameters
tagThe tag to increment.
intervalThe time interval.

◆ lf_is_tag_after_stop_tag()

bool lf_is_tag_after_stop_tag ( environment_t * env,
tag_t tag )

#include <reactor.h>

Return true if the provided tag is after stop tag.

Parameters
envEnvironment in which we are executing.
tagThe tag to check against stop tag

◆ lf_multiport_next()

int lf_multiport_next ( lf_multiport_iterator_t * iterator)

#include <port.h>

Return the channel number of the next present input on the multiport or -1 if there are no more present channels.

Parameters
iteratorThe iterator.

◆ lf_new_token()

lf_token_t * lf_new_token ( void * port_or_action,
void * val,
size_t len )

#include <lf_token.h>

Return a new disassociated token with type matching the specified port or action and containing the specified value and length.

The value is assumed to point to dynamically allocated memory that will be automatically freed. The length is 1 unless the type of the port is an array, in which case the value points to an array of the specified length. The token must then be sent to the port using lf_set_token or scheduled with the action using lf_schedule_token. The token can also be safely sent to any other port or scheduled with any other action that has the same type. If it is not scheduled or sent, then it is up to the user to free the memory allocated for the token and its value.

Parameters
port_or_actionA port or action.
valThe value.
lenThe length, or 1 if it not an array.
Returns
A pointer to a lf_token_t struct.

◆ lf_print()

void lf_print ( const char * format,
... )

#include <logging.h>

Report an informational message on stdout with a newline appended at the end.

If this execution is federated, then the message will be prefaced by identifying information for the federate. The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_debug()

void void void void lf_print_debug ( const char * format,
... )

#include <logging.h>

Report an debug message on stdout with the prefix "DEBUG: " and a newline appended at the end.

If this execution is federated, then the message will be prefaced by identifying information for the federate. The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_error()

void void void void void lf_print_error ( const char * format,
... )

#include <logging.h>

Report an error with the prefix "ERROR: " and a newline appended at the end.

The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_error_and_exit()

void void void void void void void lf_print_error_and_exit ( const char * format,
... )

#include <logging.h>

Report an error with the prefix "ERROR: " and a newline appended at the end, then exit with the failure code EXIT_FAILURE.

The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_error_system_failure()

void void void void void void void void lf_print_error_system_failure ( const char * format,
... )

#include <logging.h>

Report an error and exit just like lf_print_error_and_exit(), but also print the system error message associated with the error.

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_info()

void void lf_print_info ( const char * format,
... )

#include <logging.h>

Report an informational message on stdout with no prefix and a newline appended at the end.

If this execution is federated, then the message will be prefaced by identifying information for the federate. The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_log()

void void void lf_print_log ( const char * format,
... )

#include <logging.h>

Report an log message on stdout with the prefix "LOG: " and a newline appended at the end.

If this execution is federated, then the message will be prefaced by identifying information for the federate. The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_print_warning()

void void void void void void lf_print_warning ( const char * format,
... )

#include <logging.h>

Report a warning with the prefix "WARNING: " and a newline appended at the end.

The arguments are just like printf().

Parameters
formatThe format string to print.
...The arguments to print.

◆ lf_reactor_full_name()

const char * lf_reactor_full_name ( self_base_t * self)

#include <reactor.h>

Return the full name of the reactor.

The fully qualified name of a reactor is the instance name of the reactor concatenated with the names of all of its parents, separated by dots. If the reactor or any of its parents is a bank, then the name will have a suffix of the form [bank_index].

Parameters
selfThe self struct of the reactor.

◆ lf_reactor_name()

const char * lf_reactor_name ( self_base_t * self)

#include <reactor.h>

Return the instance name of the reactor.

The instance name is the name of given to the instance created by the new operator in LF. If the instance is in a bank, then the name will have a suffix of the form [bank_index].

Parameters
selfThe self struct of the reactor.

◆ lf_readable_time()

size_t lf_readable_time ( char * buffer,
instant_t time )

#include <tag.h>

Store into the specified buffer a string giving a human-readable rendition of the specified time.

The buffer must have length at least equal to LF_TIME_BUFFER_LENGTH. The format is:

x weeks, x d, x hr, x min, x s, x unit

where each x is a string of numbers with commas inserted if needed every three numbers and unit is ns, us, or ms.

Parameters
bufferThe buffer into which to write the string.
timeThe time to write.
Returns
The number of characters written (not counting the null terminator).

◆ lf_register_print_function()

void lf_register_print_function ( print_message_function_t * function,
int log_level )

#include <logging.h>

Register a function to display messages.

After calling this, all messages passed to the above print functions will be printed using the specified function rather than printf if their log level is greater than the specified level. The level should be one of LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, LOG_LEVEL_INFO, LOG_LEVEL_LOG, or LOG_LEVEL_DEBUG.

Parameters
functionThe print message function or NULL to revert to using printf.
log_levelThe level of messages to redirect.

◆ lf_request_stop()

void lf_request_stop ( void )

#include <reactor.h>

Request a stop to execution as soon as possible.

In a non-federated execution with only a single enclave, this will occur one microstep later than the current tag. In a federated execution or when there is more than one enclave, it will likely occur at a later tag determined by the RTI so that all federates and enclaves stop at the same tag.

◆ lf_schedule()

trigger_handle_t lf_schedule ( void * action,
interval_t offset )

#include <schedule.h>

Schedule an action to occur with the specified time offset with no payload (no value conveyed).

The later tag will depend on whether the action is logical or physical. If it is logical, the time of the event will be the current logical time of the environment associated with the action plus the minimum delay of the action plus the extra delay. If that time is equal to the current time, then the tag will be one microstep beyond the current tag. If the action is physical, the time will be the current physical time plus the extra delay, and the microstep will be zero.

See lf_schedule_token(), which this uses, for details.

Parameters
actionThe action to be triggered (a pointer to an lf_action_base_t).
offsetThe time offset over and above the minimum delay of the action.
Returns
A handle to the event, or 0 if no event was scheduled, or -1 for error.

◆ lf_schedule_copy()

trigger_handle_t lf_schedule_copy ( void * action,
interval_t offset,
void * value,
size_t length )

#include <schedule.h>

Schedule an action to occur with the specified value and time offset with a copy of the specified value.

If the value is non-null, then it will be copied into newly allocated memory under the assumption that its size is given in the trigger's token object's element_size field multiplied by the specified length.

The later tag will depend on whether the action is logical or physical. If it is logical, the time of the event will be the current logical time of the environment associated with the action plus the minimum delay of the action plus the extra delay. If that time is equal to the current time, then the tag will be one microstep beyond the current tag. If the action is physical, the time will be the current physical time plus the extra delay, and the microstep will be zero.

See lf_schedule_token(), which this uses, for details.

Parameters
actionThe action to be triggered (a pointer to an lf_action_base_t).
offsetThe time offset over and above that in the action.
valueA pointer to the value to copy.
lengthThe length, if an array, 1 if a scalar, and 0 if value is NULL.
Returns
A handle to the event, or 0 if no event was scheduled, or -1 for error.

◆ lf_schedule_int()

trigger_handle_t lf_schedule_int ( void * action,
interval_t extra_delay,
int value )

#include <schedule.h>

Schedule the specified action with an integer value at a later logical time.

The later tag will depend on whether the action is logical or physical. If it is logical, the time of the event will be the current logical time of the environment associated with the action plus the minimum delay of the action plus the extra delay. If that time is equal to the current time, then the tag will be one microstep beyond the current tag. If the action is physical, the time will be the current physical time plus the extra delay, and the microstep will be zero.

This wraps a copy of the integer value in a token. See lf_schedule_token() for more details.

Parameters
actionThe action to be triggered (a pointer to an lf_action_base_t).
extra_delayExtra offset of the event release above that in the action.
valueThe value to send.
Returns
A handle to the event, or 0 if no event was scheduled, or -1 for error.

◆ lf_schedule_token()

trigger_handle_t lf_schedule_token ( void * action,
interval_t extra_delay,
lf_token_t * token )

#include <schedule.h>

Schedule the specified action at a later tag with the specified token as a payload.

The later tag will depend on whether the action is logical or physical. If it is logical, the time of the event will be the current logical time of the environment associated with the action plus the minimum delay of the action plus the extra delay. If that time is equal to the current time, then the tag will be one microstep beyond the current tag. If the action is physical, the time will be the current physical time plus the extra delay, and the microstep will be zero.

In both cases, if the resulting tag of the event conincides with a previously scheduled event for the same action, then, by default, a microstep will be added to the tag until there is no colliding event. This behavior can be changed by specifying a minimum spacing and a policy, as explained below.

An action will trigger at a logical time that depends on the extra_delay argument given to this schedule function, the <min_delay>, <min_spacing>, and <policy> arguments in the action declaration, and whether the action is physical or logical.

For a logical action a, the tag assigned to the event is computed as follows. First, let t be the current logical time. For a logical action, t is just the logical time at which the reaction calling schedule() is called. The preliminary time of the action is then just t + <min_delay> + <offset>. This preliminary time may be further modified, as explained below.

For a physical action, the preliminary time is similar, except that t is replaced by the current physical time T when schedule() is called.

If no <min_spacing> has been declared, then the tag of the event is simply the preliminary time unless there is already an event scheduled for the same action with the same tag. In that case, a microstep is added to the tag. If there is again a previously scheduled event with the same tag, then a microstep is added to the tag again. This process is repeated until there is no previously scheduled event with the same tag.

If a <min_spacing> has been declared, then it gives a minimum logical time interval between the tags of two subsequently scheduled events. The first effect this has is that events will have monotically increasing tags. The difference between the times of two successive tags is at least <min_spacing>. If the preliminary time is closer than <min_spacing> to the time of the previously scheduled event (if there is one), or if the preliminary time is earlier than the previously scheduled event, then the time will be modified to enforce the minimum spacing. The <policy> argument determines how the minimum spacing constraint is enforced.

Note that "previously scheduled" here means specifically the tag resulting from the most recent call to the schedule function for the same action.

A <min_spacing> of 0 is not quite the same as no <min_spacing> declared. With a <min_spacing> of 0, events will still have monotically increasing tags, but the difference between the times of two successive tags can be 0.

The <policy> is one of the following:

  • "defer": (the default) The event is added to the event queue with a tag that is equal to earliest time that satisfies the minimal spacing requirement. Assuming the time of the preceding event is t_prev, then the tag of the new event simply becomes t_prev + <min_spacing>.
  • "drop": The new event is dropped and schedule() returns without having modified the event queue.
  • "replace": The payload (if any) of the new event is assigned to the preceding event if it is still pending in the event queue; no new event is added to the event queue in this case. If the preceding event has already been pulled from the event queue, the default "defer" policy is applied.

Note that while the "defer" policy is conservative in the sense that it does not discard events, it could potentially cause an unbounded growth of the event queue.

For example, suppose the minimum spacing of a logical action is 10 ms and the policy is "defer". Suppose that in a reaction to startup, the logical action is scheduled with a delay of 100 ms, then again with a delay of 99 ms, and a third time with a delay of 101 ms. The logical action will trigger at elapsed times 100 ms, 110 ms, and 120 ms.

There are three conditions under which this function will not actually put an event on the event queue and decrement the reference count of the token (if there is one), which could result in the payload being freed. In all three cases, this function returns 0. Otherwise, it returns a handle to the scheduled trigger, which is an integer greater than 0.

The first condition is that stop() has been called and the time offset of this event is greater than zero. The second condition is that the logical time of the event is greater that the stop time (timeout) that is specified in the target properties or on the command line. The third condition is that the trigger argument is null.

Parameters
actionThe action to be triggered (a pointer to an lf_action_base_t).
extra_delayExtra offset of the event release above that in the action.
tokenThe token to carry the payload or null for no payload.
Returns
A handle to the event, or 0 if no event was scheduled, or -1 for error.

◆ lf_schedule_value()

trigger_handle_t lf_schedule_value ( void * action,
interval_t extra_delay,
void * value,
int length )

#include <schedule.h>

Variant of lf_schedule_token that creates a token to carry the specified value.

The value is required to be malloc'd memory with a size equal to the element_size of the specified action times the length parameter.

See lf_schedule_token(), which this uses, for details.

Parameters
actionThe action to be triggered (a pointer to an lf_action_base_t).
extra_delayExtra offset of the event release above that in the action.
valueDynamically allocated memory containing the value to send.
lengthThe length of the array, if it is an array, or 1 for a scalar and 0 for no payload.
Returns
A handle to the event, or 0 if no event was scheduled, or -1 for error.

◆ lf_set_present()

void lf_set_present ( lf_port_base_t * port)

#include <reactor.h>

Mark the given port's is_present field as true.

Parameters
portA pointer to the port struct as an lf_port_base_t*.

◆ lf_tag()

tag_t lf_tag ( void * env)

#include <tag.h>

Return the current tag, a logical time, microstep pair.

Parameters
envA pointer to the environment from which we want the current tag.

◆ lf_tag_add()

tag_t lf_tag_add ( tag_t a,
tag_t b )

#include <tag.h>

Add two tags.

If either tag has has NEVER or FOREVER in its time field, then return NEVER_TAG or FOREVER_TAG, respectively. Also return NEVER_TAG or FOREVER_TAG if the result underflows or overflows when adding the times. If the microstep overflows, also return FOREVER_TAG. If the time field of the second tag is greater than 0, then the microstep of the first tag is reset to 0 before adding. This models the delay semantics in LF and makes this addition operation non-commutative.

Parameters
aThe first tag.
bThe second tag.

◆ lf_tag_compare()

int lf_tag_compare ( tag_t tag1,
tag_t tag2 )

#include <tag.h>

Compare two tags.

Return -1 if the first is less than the second, 0 if they are equal, and +1 if the first is greater than the second. A tag is greater than another if its time is greater or if its time is equal and its microstep is greater.

Parameters
tag1
tag2
Returns
-1, 0, or 1 depending on the relation.

◆ lf_tag_max()

tag_t lf_tag_max ( tag_t tag1,
tag_t tag2 )

#include <tag.h>

Return the greater out of two tags.

Parameters
tag1
tag2
Returns
tag_t

◆ lf_tag_min()

tag_t lf_tag_min ( tag_t tag1,
tag_t tag2 )

#include <tag.h>

Return the lesser out of two tags.

Parameters
tag1
tag2
Returns
tag_t

◆ lf_time_add()

instant_t lf_time_add ( instant_t a,
interval_t b )

#include <tag.h>

Return the sum of an interval and an instant, saturating on overflow and underflow.

Parameters
a
b
Returns
instant_t

◆ lf_time_parse()

int lf_time_parse ( const char * time_str,
const char * units_str,
interval_t * result )

#include <tag.h>

Parse a time value and units from strings, producing a time in nanoseconds.

Recognized unit strings (case-sensitive, prefix match): "nsec" or "ns", "usec" or "us", "msec" or "ms", "sec" or "s" or "second", "min" or "minute", "hour", "day", "week".

Parameters
time_strA string representing a numeric time value (e.g., "500").
units_strA string representing the time units (e.g., "msec").
resultPointer to store the resulting time in nanoseconds.
Returns
0 on success, -1 if the time value is invalid, -2 if the units are invalid.

◆ lf_time_physical()

instant_t lf_time_physical ( void )

#include <tag.h>

Return the current physical time in nanoseconds.

On many platforms, this is the number of nanoseconds since January 1, 1970, but it is actually platform dependent.

Returns
A time instant.

◆ lf_time_physical_elapsed()

instant_t lf_time_physical_elapsed ( void )

#include <tag.h>

Return the elapsed physical time in nanoseconds.

This is the time returned by lf_time_physical(void) minus the physical start time as measured by lf_time_physical(void) when the program was started.

◆ lf_time_start()

instant_t lf_time_start ( void )

#include <tag.h>

Return the physical and logical time of the start of execution in nanoseconds.

On many platforms, this is the number of nanoseconds since January 1, 1970, but it is actually platform dependent.

Returns
A time instant.

◆ lf_time_subtract()

instant_t lf_time_subtract ( instant_t a,
interval_t b )

#include <tag.h>

Return an instant minus an interval, saturating on overflow and underflow.

Parameters
a
b
Returns
instant_t

◆ lf_update_deadline()

void lf_update_deadline ( void * self,
interval_t updated_deadline )

#include <schedule.h>

Update the deadline of the currently executing reaction.

This function allows the deadline of the current reaction to be adjusted dynamically at runtime. It can be useful in scenarios where timing requirements change depending on system conditions.

Updating the deadline with this function does not affect the deadline check that was performed when the reaction started. Lingua Franca checks deadlines only at the beginning of each reaction. Therefore, if you need to confirm whether the newly updated deadline has been violated during the current execution, this can be done by invoking lf_check_deadline() immediately after this function.

Parameters
selfThe self struct of the reactor.
updated_deadlineThe updated deadline.

◆ lf_watchdog_start()

void lf_watchdog_start ( watchdog_t * watchdog,
interval_t additional_timeout )

#include <watchdog.h>

Start or restart the watchdog timer.

This function sets the expiration time of the watchdog to the current logical time plus the minimum timeout of the watchdog plus the specified additional_timeout. This function assumes the reactor mutex is held when it is called; this assumption is satisfied whenever this function is called from within a reaction that declares the watchdog as an effect.

Parameters
watchdogThe watchdog to be started
additional_timeoutAdditional timeout to be added to the watchdog's minimum expiration.

◆ lf_watchdog_stop()

void lf_watchdog_stop ( watchdog_t * watchdog)

#include <watchdog.h>

Stop the specified watchdog without invoking the expiration handler.

This function sets the expiration time of the watchdog to NEVER.

Parameters
watchdogThe watchdog.

◆ lf_writable_copy()

lf_token_t * lf_writable_copy ( lf_port_base_t * port)

#include <lf_token.h>

Return a writable copy of the token in the specified template.

If the reference count is 1, this returns the template's token rather than a copy. The reference count will be 1. Otherwise, if the size of the token payload is zero, this also returns the original token, again with reference count of 1. Otherwise, this returns a new token with a reference count of 1. The new token is added to a list of tokens whose reference counts will be decremented at the start of the next tag. If the template has no token (it has a primitive type), then there is no need for a writable copy. Return NULL.

Parameters
portAn input port, cast to (lf_port_base_t*).
Returns
A pointer to a writable copy of the token, or NULL if the type is primitive.

◆ register_user_trace_event()

int register_user_trace_event ( void * self,
char * description )

#include <tracepoint.h>

Register a user trace event.

This should be called once, providing a pointer to a string that describes a phenomenon being traced. Use the same pointer as the first argument to tracepoint_user_event() and tracepoint_user_value().

Parameters
selfPointer to the self struct of the reactor from which we want to trace this event. This pointer is used to get the correct environment and thus the correct logical tag of the event.
descriptionPointer to a human-readable description of the event.
Returns
1 if successful, 0 if the trace object table is full.

◆ tracepoint_user_event()

void tracepoint_user_event ( void * self,
char * description )

#include <tracepoint.h>

Trace a user-defined event.

Before calling this, you must call register_user_trace_event() with a pointer to the same string or else the event will not be recognized.

Parameters
selfPointer to the self struct of the reactor from which we want to trace this event. This pointer is used to get the correct environment and thus the correct logical tag of the event.
descriptionPointer to the description string.

◆ tracepoint_user_value()

void tracepoint_user_value ( void * self,
char * description,
long long value )

#include <tracepoint.h>

Trace a user-defined event with a value.

Before calling this, you must call register_user_trace_event() with a pointer to the same string or else the event will not be recognized.

Parameters
selfPointer to the self struct of the reactor from which we want to trace this event. This pointer is used to get the correct environment and thus the correct logical tag of the event.
descriptionPointer to the description string.
valueThe value of the event. This is a long long for convenience so that time values can be passed unchanged. But int values work as well.