Runtime¶
The Coco-generated C++ code depends on a small Cocotec-provided runtime that provides some common utilities. The C++ runtime is a header-only library that consists of two files:
coco/runtime.h
: the main runtime file which must be available at compile-time;coco/stream_logger.h
: an optional file, which includes an example implementation of acoco::StateMachineLogger
that is useful for debugging purposes.
The runtime is made available under the MIT license and can be downloaded from here.
Components¶
-
class
coco
::
BaseComponent
¶ The base class of all Coco components.
Subclassed by coco::BaseMultiThreadedEncapsulatingComponent, coco::BaseSingleThreadedComponent, coco::BaseSingleThreadedEncapsulatingComponent
Public Functions
-
BaseComponent
()¶
-
~BaseComponent
() = default¶
-
BaseComponent
(const BaseComponent&) = delete¶
-
BaseComponent &
operator=
(const BaseComponent&) = delete¶
-
const std::string &
name
() const¶ A name for this component.
By default this will be set to the name of the type from the Coco file or, if this component is owned by a Coco encapsulating component, to the name of the owner component.
-
void
set_name
(std::string name)¶
-
void
set_name
(const std::string &base_name, const std::string &field_name)¶
-
StateMachineLogger *
logger
() const¶ Returns the current logger that is used by the machine.
-
void
set_logger
(StateMachineLogger *logger)¶ Sets the logger that should be used by the machine.
This should not be used once start() has been called.
-
void
set_logger_recursively
(StateMachineLogger *logger)¶ Sets the logger that should be used by this state machine, and all state machines owned by this component.
In order to enable the logging, generator.cpp.alwaysEnableLogging must be true (by default this setting is true), or the generated code must be compiled with COCO_LOGGING_ENABLE defined..
This should not be used once start() has been called. The component will hold a reference to the passed logger; the caller is responsible for ensuring that the logger is kept alive until the state machine has terminated.
-
void
coco_start
() = 0¶ Starts the component’s state machine.
After this call has returned, it is safe to call all of the methods on this component’s provided port. This may be called more than once in which case subsequent calls will be ignored.
-
void
add_observer
(ComponentObserver &observer)¶ Adds an observer who will be notified about key component lifecycle events.
Protected Functions
-
void
send_machine_starting
()¶ Informs all registered ComponentObserver that this machine is starting.
Do not call this directly.
-
void
send_machine_terminated
()¶ Informs all registered ComponentObserver that this machine has terminated.
Do not call this directly.
-
void
set_names_recursively
()¶
-
-
class
coco
::
BaseMultiThreadedEncapsulatingComponent
: public coco::BaseComponent, protected coco::ComponentObserver¶ An encapsulating component that contains multi-threaded components.
Public Functions
-
BaseMultiThreadedEncapsulatingComponent
()¶
-
~BaseMultiThreadedEncapsulatingComponent
()¶
Protected Functions
-
bool
have_all_machines_terminated
() const¶ Have all machines already stopped?
Requires: mutex_ locked
-
void
wait_for_component_termination
()¶
-
void
set_names_recursively
() override = 0¶
Protected Attributes
-
std::mutex
mutex_
¶
-
-
class
coco
::
ComponentObserver
¶ Subclassed by coco::BaseMultiThreadedEncapsulatingComponent, coco::BaseSingleThreadedEncapsulatingComponent
Public Functions
-
~ComponentObserver
() = default¶
-
void
machine_starting
(const BaseComponent &component)¶ Called when a state machine is about to be started.
-
void
machine_terminated
(const BaseComponent &component)¶ Called when a state machine has succesfully shutdown.
In all runtimes, this is called after StateMachineLogger::state_exit_finished() has been called for the
Terminated
state. Otherwise:- In the single-threaded runtime, this is called just before the event that caused the transition to
Terminated
is executed. - In the multi-threaded runtime, this is called as the final action of the background thread if there is one. If there is no background thread (because the component does not handle signals, or have any execution states), then this will be called from the handler of the function call that caused the transition to Terminated.
- In the single-threaded runtime, this is called just before the event that caused the transition to
-
Single Threaded Runtime¶
-
class
coco
::
BaseSingleThreadedComponent
: public coco::BaseComponent, public coco::SingleThreadedSubscriber¶ The base class of all components that use the single-threaded runtime.
Public Functions
-
BaseSingleThreadedComponent
()¶
-
void
set_timer_executor
(SingleThreadedTimerExecutor &executor)¶ Sets the timer executor to be used to execute any timers that this single-threaded component has.
This does not normally need to be called: it is automatically called by the runtime when required.
For components that use timers this must be called prior to BaseComponent::coco_start().
-
void
execute_timer
(int timer_id)¶ Executes the given timer.
This does not normally need to be called: it is automatically called by the runtime when reuqired.
This method should only be called by this component’s SingleThreadedTimerExecutor.
-
void
coco_drain_queue
() override¶ For components using the single-threaded runtime, this will cause them to process all items in their queues.
The component will process its queue through to completion. The component will then recursively call coco_drain_queue() on its provided port. This means that the queues will be recursively drained upwards through a single-threaded component stack.
Protected Functions
-
void
cancel_timer
(int timer_id)¶
-
void
enable_timer
(int timer_id, CocoClock::duration duration)¶
-
void
send_drain_queue
()¶ Drains the queue of all subscribers.
-
void
add_single_threaded_subscriber
(SingleThreadedSubscriber &subscriber)¶ Subscribe to drain queue calls from this component.
-
void
remove_single_threaded_subscriber
(SingleThreadedSubscriber &subscriber)¶ Unsubscribe drain queue calls from this component.
-
-
class
coco
::
BaseSingleThreadedEncapsulatingComponent
: public coco::BaseComponent, protected coco::ComponentObserver¶ An encapsulating component that contains single-threaded components.
Subclassed by coco::BaseWrappedSingleThreadedEncapsulatingComponent
-
class
coco
::
SingleThreadedSubscriber
¶ Subclassed by coco::BaseSingleThreadedComponent, coco::SingleThreadedGMockHelper
Public Functions
-
~SingleThreadedSubscriber
() = default¶
-
void
coco_drain_queue
() = 0¶ For components using the single-threaded runtime, this will cause them to process all items in their queues.
The component will process its queue through to completion. The component will then recursively call coco_drain_queue() on its provided port. This means that the queues will be recursively drained upwards through a single-threaded component stack.
-
-
class
coco
::
SingleThreadedTimerExecutor
¶ An abstract class that can be implemented to provide an implementation of timers in the single-threaded runtime.
Subclassed by coco::SingleThreadedGMockHelper
Public Functions
-
SingleThreadedTimerExecutor
() = default¶
-
~SingleThreadedTimerExecutor
() = default¶
-
SingleThreadedTimerExecutor
(const SingleThreadedTimerExecutor&) = delete¶
-
SingleThreadedTimerExecutor &
operator=
(const SingleThreadedTimerExecutor&) = delete¶
-
void
enable_timer
(BaseSingleThreadedComponent &component, int timer_id, CocoClock::duration duration) = 0¶ Enables a timer.
When the timer expires, the timer implementation should call execute_timer on the component with the given timer_id. Note that there is no locking provided by the single-threaded implementation: it is the responsibility of the implementor of this class to ensure that it prevents any function calls or signals from being executed whilst the timer itself is executing.
-
void
cancel_timer
(BaseSingleThreadedComponent &component, int timer) = 0¶ Cancels a timer.
After this call, the timer implementation must not call execute_timer on the given timer until it has been re-enabled. Note that this includes cancelling any pending execute_timer calls that have been scheduled but have not yet been dispatched.
-
Ports¶
-
class
coco
::
BasePort
¶ The base class of all Coco ports.
Subclassed by coco::BaseProvidedPort
-
class
coco
::
BaseProvidedPort
: public coco::BasePort¶ Subclassed by coco::BaseProvidedInterfacePort
Public Functions
-
void
single_threaded_subscribe
(SingleThreadedSubscriber &port)¶
-
void
single_threaded_unsubscribe
(SingleThreadedSubscriber &port)¶
-
void
-
template<class
ProvidedPort
>
classcoco
::
ConnectionPoint
¶ Represents something that requires connecting to a provided port.
The most common use case of a ConnectionPoint is a required port on an implementation component which requires connecting before the system is started. ConnectionPoints are also used on encapsulating components, in which case calling connect will connect all internal users.
Public Functions
-
~ConnectionPoint
() = default¶
-
void
connect
(ProvidedPort &port, bool should_subscribe = true) = 0¶ Connects all users of this connection point to the specified port.
- Parameters
port
: The port to connect to.should_subscribe
: Indicates if all internal users should be subscribed to signals from port.
-
Logging¶
-
class
coco
::
StateMachineLogger
¶ An abstract interface to log events from a Coco state machine.
Subclassed by coco::FormatterMachineLogger
Public Functions
-
~StateMachineLogger
() = default¶
-
void
machine_starting
(const BaseComponent &component)¶
-
void
machine_started
(const BaseComponent &component, const char *current_state)¶ Called after a state machine has been started and its entry action fully executed.
-
void
machine_terminated
(const BaseComponent &component)¶
-
void
state_entry_started
(const BaseComponent &component, const char *state)¶ Call whenever a component’s state machine enters a state.
This called just before the body of the entry action is executed.
-
void
state_exit_finished
(const BaseComponent &component, const char *state)¶ Call whenever a component’s state machine exits a state.
This called just after the body of the exit action is executed.
-
void
event_started
(const BaseComponent &component, const char *current_state, const char *port, const char *event)¶ Called when an event is about to be processed by the state machine, but before a handler has been chosen.
- Parameters
component
: The component whose state machine is processing the event.current_state
: The string name of the current state of the state machine.port
: The string name corresponding to the port from which the event was received.event
: The string name of the event being received.
-
void
event_finished
(const BaseComponent &component, const char *current_state, const char *port, const char *event)¶ Called after the event has been processed to completion.
This includes after all relevant entry/exit actions have been fired. Further, in the single-threaded model it will only be fired after the queue has finished being drained.
-
void
message
(const BaseComponent &component, const char *current_state, const char *message)¶ Called whenever a call to Coco’s log() function is executed.
- Parameters
component
: The component whose state machine is processing the event.current_state
: The string name of the current state of the state machine.message
: The formatted message.
-
void
message
(const BaseComponent &component, const char *current_state, const std::string &message)¶
-
void
abort
(const BaseComponent &component, const char *current_state)¶ Called whenever a call to Coco’s abort() function is executed in a state machine.
Subclasses do not need to call abort: the calling component will immediately call abort afterwards instead.
- Parameters
component
: The component whose state machine is aborting.current_state
: The string name of the current state of the state machine.
-
EventLogger &
event_started
(const BaseComponent &component, const char *port, const char *event)¶ As per
event_started
, but this overload allows parameters and return values to be logged.
-
EventLogger &
event_finished
(const BaseComponent &component, const char *port, const char *event)¶ As per
event_finished
, but this overload allows parameters and return values to be logged.
-
EventLogger &
message
(const BaseComponent &component)¶ As per
event_finished
, but this overload allows parameters and return values to be logged.
-
class
EventLogger
¶ Subclassed by coco::FormatterEventLogger
Public Functions
-
EventLogger
() = default¶
-
~EventLogger
() = default¶
-
EventLogger
(const EventLogger&) = delete¶
-
EventLogger &
operator=
(const EventLogger&) = delete¶
-
template<typename
T
>
EventLogger ¤t_state
(const T &value)¶
-
EventLogger &
message
(const char *str)¶
-
EventLogger &
message
(const std::string &str)¶
-
template<typename
T
>
EventLogger ¶meter
(const char *parameter_name, const T &value)¶
-
template<typename
T
>
EventLogger &return_value
(const T &value)¶
-
void
done
() = 0¶
Protected Functions
-
Formatter &
formatter
() = 0¶
-
void
begin_parameter
(const char *parameter_name) = 0¶
-
void
end_parameter
(const char *parameter_name) = 0¶
-
void
begin_return_value
() = 0¶
-
void
end_return_value
() = 0¶
-
void
begin_current_state
() = 0¶
-
void
end_current_state
() = 0¶
-
void
message
(const char *message, size_t message_size) = 0¶
-
-
-
class
coco
::
StreamMachineLogger
: public coco::FormatterMachineLogger, private coco::Formatter¶ A simple logger that outputs to a std::ostream.
This is not intended for production usage, but as an example of how a logger could be implemented.
Public Static Functions
-
StreamMachineLogger *
cout
()¶ Returns a pointer to a shared logger that logs to stdout.
This can be directly passed to BaseComponent::set_logger_recursively, for example:
component.set_logger_recursively(StreamMachineLogger::cout);
-
StreamMachineLogger *
cerr
()¶ Returns a pointer to a shared logger that logs to stderr.
This can be directly passed to BaseComponent::set_logger_recursively, for example:
component.set_logger_recursively(StreamMachineLogger::cerr);
-
StreamMachineLogger *
Mocks¶
-
class
coco
::
MultiThreadedGMockHelper
¶ A simple helper class to assist with creating multi-threaded gmocks.
This uses a background thread to execute actions asynchronously. The intended use is that you create a single instance of MultiThreadedGMockHelper inside your test case, and use InvokeAsync to invoke actions asynchronously. These will then be executed by a background thread. For example:
ClientMock client_mock; coco::MultiThreadedGMockHelper helper; ... EXPECT_CALL(client_mock, pinger_pong(value)) .WillOnce(helper.InvokeAsync(Invoke(&client_mock.pinger().connected(), &PPingerProvided::terminate)))
Public Functions
-
MultiThreadedGMockHelper
()¶
-
~MultiThreadedGMockHelper
()¶
-
testing::PolymorphicAction<ExecuteAsyncAction>
InvokeAsync
(const testing::Action<void()> &action)¶ Executes an arbitrary gMock action asynchronously.
Note that this will be immediately added to a queue which will execute concurrently with this, meaning that the execution of the action could happen before this function even returns.
-
testing::PolymorphicAction<ExecuteAsyncAction>
InvokeAsync
(const testing::Action<void()> &actionCocoClock::duration delay, )¶ Executes an arbitrary gMock action asynchronously after the specified delay has elapsed.
-
void
execute_async
(std::function<void()> &&function)¶
-
void
execute_async
(std::function<void()> &&functionCocoClock::duration delay, )¶
-
-
class
coco
::
SingleThreadedGMockHelper
: public coco::SingleThreadedSubscriber, public coco::SingleThreadedTimerExecutor¶ A simple helper class to assist with creating multi-threaded gmocks.
This accumulates background actions in a queue and provides an easy way to execute them. The intended use is that you create a single instance of SingleThreadedGMockHelper inside your test case, and use InvokeAsync to invoke actions asynchronously. You should then periodically call coco_drain_queue to execute the accumulated actions. For example:
ClientMock client_mock; coco::SingleThreadedGMockHelper helper; ... EXPECT_CALL(client_mock, pinger_pong(value)) .WillOnce(helper.InvokeAsync(Invoke(&client_mock.pinger().connected(), &PPingerProvided::terminate))) while (...) { helper.coco_drain_queue(); }
Public Functions
-
SingleThreadedGMockHelper
()¶
-
~SingleThreadedGMockHelper
()¶
-
testing::PolymorphicAction<ExecuteAsyncAction>
InvokeAsync
(const testing::Action<void()> &action)¶ Executes an arbitrary gMock action asynchronously.
Note that this will be immediately added to a queue which will execute concurrently with this, meaning that the execution of the action could happen before this function even returns.
-
testing::PolymorphicAction<ExecuteAsyncAction>
InvokeAsync
(const testing::Action<void()> &actionCocoClock::duration delay, )¶ Executes an arbitrary gMock action asynchronously after the specified delay has elapsed.
-
void
execute_async
(std::function<void()> &&function)¶
-
void
execute_async
(std::function<void()> &&functionCocoClock::duration delay, )¶
-
void
coco_drain_queue
() override¶ For components using the single-threaded runtime, this will cause them to process all items in their queues.
The component will process its queue through to completion. The component will then recursively call coco_drain_queue() on its provided port. This means that the queues will be recursively drained upwards through a single-threaded component stack.
-
void
enable_timer
(BaseSingleThreadedComponent &component, int timer_id, CocoClock::duration duration) override¶ Enables a timer.
When the timer expires, the timer implementation should call execute_timer on the component with the given timer_id. Note that there is no locking provided by the single-threaded implementation: it is the responsibility of the implementor of this class to ensure that it prevents any function calls or signals from being executed whilst the timer itself is executing.
-
void
cancel_timer
(BaseSingleThreadedComponent &component, int timer_id) override¶ Cancels a timer.
After this call, the timer implementation must not call execute_timer on the given timer until it has been re-enabled. Note that this includes cancelling any pending execute_timer calls that have been scheduled but have not yet been dispatched.
-
Utility¶
-
template<typename ...
Ts
>
classcoco
::
variant
¶ Provides support for Coco’s tagged enums. Equivalent to C++17’s std::variant type.
Public Functions
-
variant
()¶
-
template<class
T
, typenameEnable
= typename std::enable_if<!std::is_same<variant<Ts...>, typename std::decay<T>::type>::value>::type>variant
(const T &other)¶
-
template<class
T
, typenameEnable
= typename std::enable_if<!std::is_same<variant<Ts...>, typename std::decay<T>::type>::value>::type>variant
(T &&other)¶
-
~variant
()¶
-
std::size_t
index
() const¶
-
-
template<typename
T
>
classcoco
::
optional
¶ Provides support for Coco’s optional type. Equivalent to C++17’s std::optional type.
-
template<typename
T
, typenameE
>
classcoco
::
expected
¶ A simple P0323-like implementation of std::expected, suitable for coco’s Result type.
Suppose you have a function such as:
Within the body of mayFail, you can use:coco::expected<std::vector<bool>, int> mayFail();
to return a specific error code, or:return coco::make_unexpected(5);
to return the expected value.return std::vector<bool>()
When calling mayFail(), the accessors has_value(), value() and error() can be used to either access the value or the container error, as required.
- Template Parameters
T
: the type of a success case.E
: the type of an error.
Public Functions
-
expected
()¶
-
~expected
() = default¶
-
expected
(const unexpected<E> &error)¶
-
expected
(unexpected<E> &&error)¶
-
bool
has_error
() const¶ Returns true if this expected contains an error rather than a value.
-
bool
has_value
() const¶ Returns true if this expected contains a value rather than an error.
-
template<typename
E
>
classcoco
::
unexpected
¶ A helper class for coco::expected which can be used to return an error from a function that returns expected<T, E>.
@seealso expected
Public Functions
-
unexpected
() = delete¶
-
unexpected
(const unexpected&) = default¶
-
unexpected &
operator=
(const unexpected&) = default¶
-
unexpected
(unexpected &&other) noexcept¶
-
unexpected &
operator=
(unexpected &&other) noexcept¶
-
~unexpected
() = default¶
-
-
class
coco
::
IllegalException
: public exception¶ If illegalBehaviour is set to Exception, then an exception that is thrown when Coco encounters an illegal event.
Note that after receiving this exception, you must not call back into the Coco system, since the state of the system cannot be guaranteed.
Public Functions
-
IllegalException
(std::string component_name)¶
-
IllegalException
(std::string component_name, const char *state_name)¶
-
~IllegalException
()¶
-
IllegalException
(const IllegalException&) = default¶
-
IllegalException &
operator=
(const IllegalException&) = default¶
-
const char *
what
() const override¶
-
const std::string &
component_name
() const¶
-
const char *
state_name
() const¶
-