qpcpp/include/qf.hpp
2022-10-26 19:47:56 -04:00

1845 lines
70 KiB
C++

//$file${include::qf.hpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//
// Model: qpcpp.qm
// File: ${include::qf.hpp}
//
// This code has been generated by QM 5.2.2 <www.state-machine.com/qm>.
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License # : LicenseRef-QL-dual
// Issued to : Any user of the QP/C++ real-time embedded framework
// Framework(s) : qpcpp
// Support ends : 2023-12-31
// License scope:
//
// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
//
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
//
// This software is dual-licensed under the terms of the open source GNU
// General Public License version 3 (or any later version), or alternatively,
// under the terms of one of the closed source Quantum Leaps commercial
// licenses.
//
// The terms of the open source GNU General Public License version 3
// can be found at: <www.gnu.org/licenses/gpl-3.0>
//
// The terms of the closed source Quantum Leaps commercial licenses
// can be found at: <www.state-machine.com/licensing>
//
// Redistributions in source code must retain this top-level comment block.
// Plagiarizing this software to sidestep the license obligations is illegal.
//
// Contact information:
// <www.state-machine.com/licensing>
// <info@state-machine.com>
//
//$endhead${include::qf.hpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//! @file
//! @brief QF/C++ platform-independent public interface.
#ifndef QP_INC_QF_HPP_
#define QP_INC_QF_HPP_
#ifdef Q_EVT_CTOR
#include <new> // for placement new
#include <cstdarg> // for va_list
#endif // Q_EVT_CTOR
//============================================================================
// Global namespace...
//$declare${QF-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//${QF-config::QF_MAX_ACTIVE} ................................................
//! Maximum number of active objects (configurable value in qf_port.hpp)
//! Valid values: [1U..64U]; default 32U
#ifndef QF_MAX_ACTIVE
#define QF_MAX_ACTIVE 32U
#endif // ndef QF_MAX_ACTIVE
//${QF-config::QF_MAX_ACTIVE exceeds the maximu~} ............................
#if (QF_MAX_ACTIVE > 64U)
#error QF_MAX_ACTIVE exceeds the maximum of 64U;
#endif // (QF_MAX_ACTIVE > 64U)
//${QF-config::QF_MAX_TICK_RATE} .............................................
//! Maximum number of clock rates (configurable value in qf_port.hpp)
//! Valid values: [0U..15U]; default 1U
#ifndef QF_MAX_TICK_RATE
#define QF_MAX_TICK_RATE 1U
#endif // ndef QF_MAX_TICK_RATE
//${QF-config::QF_MAX_TICK_RATE exceeds the max~} ............................
#if (QF_MAX_TICK_RATE > 15U)
#error QF_MAX_TICK_RATE exceeds the maximum of 15U;
#endif // (QF_MAX_TICK_RATE > 15U)
//${QF-config::QF_MAX_EPOOL} .................................................
//! Maximum number of event pools (configurable value in qf_port.hpp)
//! Valid values: [0U..15U]; default 3U
//!
//! @note
//! #QF_MAX_EPOOL set to zero means that dynamic events are NOT configured
//! and should not be used in the application.
#ifndef QF_MAX_EPOOL
#define QF_MAX_EPOOL 3U
#endif // ndef QF_MAX_EPOOL
//${QF-config::QF_MAX_EPOOL exceeds the maximum~} ............................
#if (QF_MAX_EPOOL > 15U)
#error QF_MAX_EPOOL exceeds the maximum of 15U;
#endif // (QF_MAX_EPOOL > 15U)
//${QF-config::QF_TIMEEVT_CTR_SIZE} ..........................................
//! Size of the QTimeEvt counter (configurable value in qf_port.hpp)
//! Valid values: 1U, 2U, or 4U; default 4U
#ifndef QF_TIMEEVT_CTR_SIZE
#define QF_TIMEEVT_CTR_SIZE 4U
#endif // ndef QF_TIMEEVT_CTR_SIZE
//${QF-config::QF_TIMEEVT_CTR_SIZE defined inco~} ............................
#if (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U)
#error QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U;
#endif // (QF_TIMEEVT_CTR_SIZE != 1U) && (QF_TIMEEVT_CTR_SIZE != 2U) && (QF_TIMEEVT_CTR_SIZE != 4U)
//${QF-config::QF_EVENT_SIZ_SIZE} ............................................
//! Size of the event-size (configurable value in qf_port.hpp)
//! Valid values: 1U, 2U, or 4U; default 2U
#ifndef QF_EVENT_SIZ_SIZE
#define QF_EVENT_SIZ_SIZE 2U
#endif // ndef QF_EVENT_SIZ_SIZE
//${QF-config::QF_EVENT_SIZ_SIZE defined incorr~} ............................
#if (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U)
#error QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U;
#endif // (QF_EVENT_SIZ_SIZE != 1U) && (QF_EVENT_SIZ_SIZE != 2U) && (QF_EVENT_SIZ_SIZE != 4U)
//$enddecl${QF-config} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//============================================================================
//$declare${QF-types} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
//${QF-types::QPSetBits} .....................................................
//! bitmask for the internal representation of QPSet elements
#if (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U)
using QPSetBits = std::uint16_t;
#endif // (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U)
//${QF-types::QPSetBits} .....................................................
#if (16 < QF_MAX_ACTIVE)
using QPSetBits = std::uint32_t;
#endif // (16 < QF_MAX_ACTIVE)
//${QF-types::QPSetBits} .....................................................
#if (QF_MAX_ACTIVE <= 8U)
using QPSetBits = std::uint8_t;
#endif // (QF_MAX_ACTIVE <= 8U)
//${QF-types::QTimeEvtCtr} ...................................................
//! Data type to store the block-size defined based on the macro
//! #QF_TIMEEVT_CTR_SIZE.
//!
//! @details
//! The dynamic range of this data type determines the maximum block
//! size that can be managed by the pool.
#if (QF_TIMEEVT_CTR_SIZE== 2U)
using QTimeEvtCtr = std::uint16_t;
#endif // (QF_TIMEEVT_CTR_SIZE== 2U)
//${QF-types::QTimeEvtCtr} ...................................................
#if (QF_TIMEEVT_CTR_SIZE== 1U)
using QTimeEvtCtr = std::uint8_t;
#endif // (QF_TIMEEVT_CTR_SIZE== 1U)
//${QF-types::QTimeEvtCtr} ...................................................
#if (QF_TIMEEVT_CTR_SIZE== 4U)
using QTimeEvtCtr = std::uint32_t;
#endif // (QF_TIMEEVT_CTR_SIZE== 4U)
//${QF-types::QPrioSpec} .....................................................
//! Priority specification for Active Objects in QP
//!
//! @details
//! Active Object priorities in QP are integer numbers in the range
//! [1..#QF_MAX_ACTIVE], whereas the special priority number 0 is reserved
//! for the lowest-priority idle thread. The QP framework uses the *direct*
//! priority numbering, in which higher numerical values denote higher
//! urgency. For example, an AO with priority 32 has higher urgency than
//! an AO with priority 23.
//!
//! QP::QPrioSpec allows an application developer to assign **two**
//! priorities to a given AO (see also Q_PRIO()):
//!
//! 1. The "QF-priority", which resides in the least-significant byte
//! of the QP::QPrioSpec data type. The "QF-priority" must be **unique**
//! for each thread in the system and higher numerical values represent
//! higher urgency (direct pirority numbering).
//!
//! 2. The "preemption-threshold" priority, which resides in the most-
//! significant byte of the ::QPrioSpec data type. The second priority
//! cannot be lower than the "QF-priority", but does NOT need to be
//! unuque.
//!
//! In the QP native preemptive kernels, like QK and QXK, the "preemption-
//! threshold" priority is used as to implement the "preemption-threshold
//! scheduling" (PTS). It determines the conditions under which a given
//! thread can be *preempted* by other threads. Specifically, a given
//! thread can be preempted only by another thread with a *higher*
//! priority than the "preemption-threshold" of the original thread.
//!
//! ![QF-priority and preemption-threshold relations](qp-prio.png)
//!
//! @note
//! For backwards-compatibility, QP::QPrioSpec data type might contain only
//! the "QF-priority" component (and the "preemption-threshold" component
//! left at zero). In that case, the "preemption-threshold" will be assumed
//! to be the same as the "QF-priority". This corresponds exactly to the
//! previous semantics of AO priority.
//!
//! @remark
//! When QP runs on top of 3rd-party kernels/RTOSes or general-purpose
//! operating systems, sthe second priority can have different meaning,
//! depending on the specific RTOS/GPOS used.
using QPrioSpec = std::uint16_t;
//${QF-types::QSchedStatus} ..................................................
//! The scheduler lock status used in some real-time kernels
using QSchedStatus = std::uint_fast16_t;
//${QF-types::QPSet} .........................................................
//! Priority Set of up to #QF_MAX_ACTIVE elements
//!
//! @details
//! The priority set represents the set of active objects that are ready to
//! run and need to be considered by the scheduling algorithm. The set is
//! capable of storing up to #QF_MAX_ACTIVE priority levels, which can be
//! configured in the rage 1..64, inclusive.
class QPSet {
public:
#if (QF_MAX_ACTIVE <= 32)
//! bitmask with a bit for each element
QPSetBits volatile m_bits;
#endif // (QF_MAX_ACTIVE <= 32)
#if (32 < QF_MAX_ACTIVE)
//! bitmasks with a bit for each element
QPSetBits volatile m_bits[2];
#endif // (32 < QF_MAX_ACTIVE)
public:
//! Make the priority set empty
void setEmpty() noexcept {
#if (QF_MAX_ACTIVE <= 32U)
m_bits = 0U;
#else
m_bits[0] = 0U;
m_bits[1] = 0U;
#endif
}
//! Return 'true' if the priority set is empty
bool isEmpty() const noexcept {
#if (QF_MAX_ACTIVE <= 32U)
return (m_bits == 0U);
#else
return (m_bits[0] == 0U) ? (m_bits[1] == 0U) : false;
#endif
}
//! Return 'true' if the priority set is NOT empty
bool notEmpty() const noexcept {
#if (QF_MAX_ACTIVE <= 32U)
return (m_bits != 0U);
#else
return (m_bits[0] != 0U) ? true : (m_bits[1] != 0U);
#endif
}
//! Return 'true' if the priority set has the element n.
bool hasElement(std::uint_fast8_t const n) const noexcept {
#if (QF_MAX_ACTIVE <= 32U)
return (m_bits & (1U << (n - 1U))) != 0U;
#else
return (n <= 32U)
? ((m_bits[0] & (static_cast<std::uint32_t>(1) << (n - 1U))) != 0U)
: ((m_bits[1] & (static_cast<std::uint32_t>(1) << (n - 33U))) != 0U);
#endif
}
//! insert element `n` into the set (n = 1..QF_MAX_ACTIVE)
void insert(std::uint_fast8_t const n) noexcept {
#if (QF_MAX_ACTIVE <= 32U)
m_bits = (m_bits | (1U << (n - 1U)));
#else
if (n <= 32U) {
m_bits[0] = (m_bits[0]
| (static_cast<std::uint32_t>(1) << (n - 1U)));
}
else {
m_bits[1] = (m_bits[1]
| (static_cast<std::uint32_t>(1) << (n - 33U)));
}
#endif
}
//! Remove element `n` from the set (n = 1U..64U)
void remove(std::uint_fast8_t const n) noexcept {
#if (QF_MAX_ACTIVE <= 32U)
m_bits = (m_bits & static_cast<QPSetBits>(
~(static_cast<QPSetBits>(1) << (n - 1U))));
#else
if (n <= 32U) {
(m_bits[0] = (m_bits[0]
& ~(static_cast<std::uint32_t>(1) << (n - 1U))));
}
else {
(m_bits[1] = (m_bits[1]
& ~(static_cast<std::uint32_t>(1) << (n - 33U))));
}
#endif
}
//! Find the maximum element in the set, returns zero if the set is empty
std::uint_fast8_t findMax() const noexcept {
#if (QF_MAX_ACTIVE <= 32U)
return QF_LOG2(m_bits);
#else
return (m_bits[1] != 0U)
? (QF_LOG2(m_bits[1]) + 32U)
: (QF_LOG2(m_bits[0]));
#endif
}
#ifndef QF_LOG2
//! Log-base-2 calculation when hardware acceleration
//! is NOT provided (#QF_LOG2 not defined).
std::uint_fast8_t QF_LOG2(QP::QPSetBits x) const noexcept;
#endif // ndef QF_LOG2
}; // class QPSet
//${QF-types::QSubscrList} ...................................................
//! Subscriber List (for publish-subscribe)
//!
//! @details
//! This data type represents a set of Active Objects that subscribe to
//! a given signal. The set is represented as priority-set, where each bit
//! corresponds to the unique QF-priority of an AO (see QP::QPrioSpec).
using QSubscrList = QPSet;
} // namespace QP
//$enddecl${QF-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$declare${QF::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
//${QF::QActive} .............................................................
//! QP::QActive active object class (based on the QP::QHsm-style
//! implementation strategy)
//!
//! @details
//! Active objects are encapsulated tasks (each containing an event queue and
//! a state machine) that communicate with one another asynchronously by
//! sending and receiving events. Within an active object, events are
//! processed in a run-to-completion (RTC) fashion, while QF encapsulates
//! all the details of thread-safe event exchange and queuing.<br>
//!
//! QP::QActive represents an active object that uses the QP::QHsm-style
//! implementation strategy for state machines. This strategy is tailored
//! to manual coding, but it is also supported by the QM modeling tool.
//! The resulting code is slower than in the QP::QMsm-style implementation
//! strategy.
//!
//! @note
//! QP::QActive is not intended to be instantiated directly, but rather serves
//! as the abstract base class for derivation of active objects in the
//! applications.
//!
//! @sa QP::QMActive
//!
//! @usage
//! The following example illustrates how to derive an active object from
//! QP::QActive.
//! @include qf_qactive.cpp
class QActive : public QP::QHsm {
public:
#ifdef QF_EQUEUE_TYPE
//! OS-dependent event-queue type
//!
//! @details
//! The type of the queue depends on the underlying operating system or
//! a kernel. Many kernels support "message queues" that can be adapted
//! to deliver QF events to the active object. Alternatively, QF provides
//! a native event queue implementation that can be used as well.
//!
//! @note
//! The native QF event queue is configured by defining the macro
//! #QF_EQUEUE_TYPE as QP::QEQueue.
QF_EQUEUE_TYPE m_eQueue;
#endif // def QF_EQUEUE_TYPE
#ifdef QF_OS_OBJECT_TYPE
//! OS-dependent per-thread object
//!
//! @details
//! This data might be used in various ways, depending on the QF port.
//! In some ports m_osObject is used to block the calling thread when
//! the native QF queue is empty. In other QF ports the OS-dependent
//! object might be used differently.
QF_OS_OBJECT_TYPE m_osObject;
#endif // def QF_OS_OBJECT_TYPE
#ifdef QF_THREAD_TYPE
//! OS-dependent representation of the thread of the active object
//!
//! @details
//! This data might be used in various ways, depending on the QF port.
//! In some ports m_thread is used store the thread handle. In other ports
//! m_thread can be a pointer to the Thread-Local-Storage (TLS).
QF_THREAD_TYPE m_thread;
#endif // def QF_THREAD_TYPE
//! QF-priority [1..#QF_MAX_ACTIVE] of this AO.
//! @sa QP::QPrioSpec
std::uint8_t m_prio;
//! preemption-threshold [1..#QF_MAX_ACTIVE] of this AO.
//! @sa QP::QPrioSpec
std::uint8_t m_pthre;
private:
friend class QTimeEvt;
friend class QTicker;
#ifdef QP_INC_QXK_HPP_
friend class QXThread;
#endif // def QP_INC_QXK_HPP_
#ifdef QP_INC_QXK_HPP_
friend class QXMutex;
#endif // def QP_INC_QXK_HPP_
#ifdef QP_INC_QXK_HPP_
friend class QXSemaphore;
#endif // def QP_INC_QXK_HPP_
#ifdef Q_UTEST
friend class QActiveDummy;
#endif // def Q_UTEST
friend class GuiQActive;
friend class GuiQMActive;
public:
//! Internal array of registered active objects
static QActive * registry_[QF_MAX_ACTIVE + 1U];
//! pointer to the array of all subscriber AOs for a given event signal
static QSubscrList * subscrList_;
//! The maximum published signal (the size of the subscrList_ array)
static enum_t maxPubSignal_;
protected:
//! protected constructor (abstract class)
QActive(QStateHandler const initial) noexcept;
public:
//! Starts execution of an active object and registers the object
//! with the framework
//!
//! @details
//! Starts execution of the AO and registers the AO with the framework.
//!
//! @param[in] prioSpec priority specification of the AO containing the
//! QF-priority and (optionally) preemption-threshold of this AO
//! (for preemptive kernels that support it). See also QP::QPrioSpec.
//! @param[in] qSto pointer to the storage for the ring buffer of the
//! event queue
//! @param[in] qLen length of the event queue [# QP::QEvt* pointers]
//! @param[in] stkSto pointer to the stack storage (might be nullptr)
//! @param[in] stkSize stack size [bytes]
//! @param[in] par pointer to an extra parameter (might be nullptr)
//!
//! @usage
//! The following example shows starting an AO when a per-task stack
//! is needed:
//! @include qf_start.cpp
virtual void start(
QPrioSpec const prioSpec,
QEvt const * * const qSto,
std::uint_fast16_t const qLen,
void * const stkSto,
std::uint_fast16_t const stkSize,
void const * const par);
//! Overloaded start function (no initialization parameter)
virtual void start(
QPrioSpec const prioSpec,
QEvt const * * const qSto,
std::uint_fast16_t const qLen,
void * const stkSto,
std::uint_fast16_t const stkSize)
{
this->start(prioSpec, qSto, qLen, stkSto, stkSize, nullptr);
}
#ifdef QF_ACTIVE_STOP
//! Stops execution of an active object and removes it from the
//! framework's supervision
//!
//! @attention
//! QActive::stop() must be called only from the AO that is about
//! to stop its execution. By that time, any pointers or references
//! to the AO are considered invalid (dangling) and it becomes
//! illegal for the rest of the application to post events to the AO.
void stop();
#endif // def QF_ACTIVE_STOP
//! Posts an event `e` directly to the event queue of the active object
//! using the First-In-First-Out (FIFO) policy.
//!
//! @details
//! Direct event posting is the simplest asynchronous communication
//! method available in QF.
//!
//! @param[in] e pointer to the event to be posted
//! @param[in] margin number of required free slots in the queue
//! after posting the event or QF::NO_MARGIN.
//! @param[in] sender pointer to a sender object (used in QS only)
//!
//! @returns
//! 'true' (success) if the posting succeeded (with the provided margin)
//! and 'false' (failure) when the posting fails.
//!
//! @attention
//! For `margin` == QF::NO_MARGIN, this function will assert internally
//! if the event posting fails. In that case, it is unnecessary to check
//! the retrun value from this function.
//!
//! @note
//! This function might be implemented differentyl in various QP/C++
//! ports. The provided implementation assumes that the QP::QEQueue
//! class is used for the QP::QActive event queue.
//!
//! @usage
//! @include qf_post.cpp
//!
//! @sa
//! QActive::postLIFO()
virtual bool post_(
QEvt const * const e,
std::uint_fast16_t const margin,
void const * const sender) noexcept;
//! Posts an event `e` directly to the event queue of the active object
//! using the Last-In-First-Out (LIFO) policy.
//!
//! @details
//! The LIFO policy should be used only for self-posting and with caution,
//! because it alters order of events in the queue.
//!
//! @param[in] e pointer to the event to be posted
//!
//! @attention
//! This function asserts internally if the posting fails.
//!
//! @note
//! This function might be implemented differentyl in various QP/C++
//! ports. The provided implementation assumes that the QP::QEQueue
//! class is used for the QActive event queue.
//!
//! @sa
//! QActive::post()
virtual void postLIFO(QEvt const * const e) noexcept;
//! Get an event from the event queue of an active object
//!
//! @details
//! The behavior of this function depends on the kernel used in the
//! QF port. For built-in kernels (Vanilla or QK) the function can be
//! called only when the queue is not empty, so it doesn't block. For
//! a blocking kernel/OS the function can block and wait for delivery
//! of an event.
//!
//! @returns
//! A pointer to the received event. The returned pointer is guaranteed
//! to be valid (can't be nullptr).
//!
//! @note
//! This function might be implemented differentyl in various QP/C++
//! ports. The provided implementation assumes that the QP::QEQueue
//! class is used for the QActive event queue.
QEvt const * get_() noexcept;
//! Subscribes for delivery of signal `sig` to the active object
//!
//! @details
//! This function is part of the Publish-Subscribe event delivery
//! mechanism available in QF. Subscribing to an event means that the
//! framework will start posting all published events with a given signal
//! `sig` to the event queue of the active object.
//!
//! @param[in] sig event signal to subscribe
//!
//! The following example shows how the Table active object subscribes
//! to three signals in the initial transition:
//! @include qf_subscribe.cpp
//!
//! @sa
//! QActive::publish_(), QActive::unsubscribe(), and
//! QActive::unsubscribeAll()
void subscribe(enum_t const sig) const noexcept;
//! Unsubscribes from the delivery of signal `sig` to the active object
//!
//! @details
//! This function is part of the Publish-Subscribe event delivery
//! mechanism available in QF. Un-subscribing from an event means that
//! the framework will stop posting published events with a given signal
//! `sig` to the event queue of the active object.
//!
//! @param[in] sig event signal to unsubscribe
//!
//! @note
//! Due to the latency of event queues, an active object should NOT
//! assume that a given signal `sig` will never be dispatched to the
//! state machine of the active object after un-subscribing from that
//! signal. The event might be already in the queue, or just about to
//! be posted and the un-subscribe operation will not flush such events.
//!
//! @note
//! Un-subscribing from a signal that has never been subscribed in the
//! first place is considered an error and QF will raise an assertion.
//!
//! @sa
//! QActive::publish_(), QActive::subscribe(), and
//! QActive::unsubscribeAll()
void unsubscribe(enum_t const sig) const noexcept;
//! Unsubscribes from the delivery of all signals to the active object
//!
//! @details
//! This function is part of the Publish-Subscribe event delivery
//! mechanism available in QF. Un-subscribing from all events means that
//! the framework will stop posting any published events to the event
//! queue of the active object.
//!
//! @note
//! Due to the latency of event queues, an active object should NOT
//! assume that no events will ever be dispatched to the state machine of
//! the active object after un-subscribing from all events.
//! The events might be already in the queue, or just about to be posted
//! and the un-subscribe operation will not flush such events. Also, the
//! alternative event-delivery mechanisms, such as direct event posting or
//! time events, can be still delivered to the event queue of the active
//! object.
//!
//! @sa
//! QActive::publish_(), QActive::subscribe(), and QActive::unsubscribe()
void unsubscribeAll() const noexcept;
//! Defer an event to a given separate event queue
//!
//! @details
//! This function is part of the event deferral support. An active object
//! uses this function to defer an event `e` to the QF-supported native
//! event queue `eq`. QF correctly accounts for another outstanding
//! reference to the event and will not recycle the event at the end of
//! the RTC step. Later, the active object might recall one event at a
//! time from the event queue.
//!
//! @param[in] eq pointer to a "raw" thread-safe queue to recall
//! an event from.
//! @param[in] e pointer to the event to be deferred
//!
//! @returns
//! 'true' (success) when the event could be deferred and 'false'
//! (failure) if event deferral failed due to overflowing the queue.
//!
//! An active object can use multiple event queues to defer events of
//! different kinds.
//!
//! @sa
//! QActive::recall(), QP::QEQueue, QActive::flushDeferred()
bool defer(
QEQueue * const eq,
QEvt const * const e) const noexcept;
//! Recall a deferred event from a given event queue
//!
//! @details
//! This function is part of the event deferral support. An active object
//! uses this function to recall a deferred event from a given QF
//! event queue. Recalling an event means that it is removed from the
//! deferred event queue `eq` and posted (LIFO) to the event queue of
//! the active object.
//!
//! @param[in] eq pointer to a "raw" thread-safe queue to recall
//! an event from.
//!
//! @returns
//! 'true' if an event has been recalled and 'false' if not.
//!
//! @note
//! An active object can use multiple event queues to defer events of
//! different kinds.
//!
//! @sa
//! QActive::recall(), QActive::postLIFO_(), QP::QEQueue
bool recall(QEQueue * const eq) noexcept;
//! Flush the specified deferred queue 'eq'
//!
//! @details
//! This function is part of the event deferral support. An active object
//! can use this function to flush a given QF event queue. The function
//! makes sure that the events are not leaked.
//!
//! @param[in] eq pointer to a "raw" thread-safe queue to flush.
//!
//! @returns
//! the number of events actually flushed from the queue.
//!
//! @sa
//! QActive::defer(), QActive::recall(), QP::QEQueue
std::uint_fast16_t flushDeferred(QEQueue * const eq) const noexcept;
//! Get the priority of the active object.
std::uint8_t getPrio() const noexcept {
return m_prio;
}
protected:
//! Set the priority of the active object.
void setPrio(QPrioSpec const prio) noexcept {
m_prio = static_cast<std::uint8_t>(prio & 0xFFU);
m_pthre = static_cast<std::uint8_t>(prio >> 8U);
}
public:
//! Generic setting of additional attributes (useful in QP ports)
void setAttr(
std::uint32_t attr1,
void const * attr2 = nullptr);
#ifdef QF_OS_OBJECT_TYPE
//! accessor to the OS-object for extern "C" functions, such as
//! the QK or QXK schedulers
QF_OS_OBJECT_TYPE & getOsObject() noexcept {
return m_osObject;
}
#endif // def QF_OS_OBJECT_TYPE
#ifdef QF_THREAD_TYPE
//! accessor to the Thread for extern "C" functions, such as
//! the QK or QXK schedulers
QF_THREAD_TYPE & getThread() noexcept {
return m_thread;
}
#endif // def QF_THREAD_TYPE
//! Publish-subscribe initialization
//!
//! @details
//! This function initializes the publish-subscribe facilities of QF and must
//! be called exactly once before any subscriptions/publications occur in
//! the application.
//!
//! @param[in] subscrSto pointer to the array of subscriber lists
//! @param[in] maxSignal the dimension of the subscriber array and at
//! the same time the maximum signal that can be
//! published or subscribed.
//!
//! The array of subscriber-lists is indexed by signals and provides a mapping
//! between the signals and subscriber-lists. The subscriber-lists are
//! bitmasks of type QP::QSubscrList, each bit in the bit mask corresponding
//! to the unique priority of an active object. The size of the
//! QP::QSubscrList bitmask depends on the value of the #QF_MAX_ACTIVE macro.
//!
//! @note
//! The publish-subscribe facilities are optional, meaning that you might
//! choose not to use publish-subscribe. In that case calling QF::psInit()
//! and using up memory for the subscriber-lists is unnecessary.
//!
//! @sa
//! QP::QSubscrList
//!
//! @usage
//! The following example shows the typical initialization sequence of QF:
//! @include qf_main.cpp
static void psInit(
QSubscrList * const subscrSto,
enum_t const maxSignal) noexcept;
//! Publish event to all subscribers of a given signal `e->sig`
//!
//! @details
//! This function posts (using the FIFO policy) the event @a e to **all**
//! active objects that have subscribed to the signal @a e->sig, which is
//! called _multicasting_. The multicasting performed in this function is
//! very efficient based on reference-counting inside the published event
//! ("zero-copy" event multicasting). This function is designed to be
//! callable from any part of the system, including ISRs, device drivers,
//! and active objects.
//!
//! @note
//! To avoid any unexpected re-ordering of events posted into AO queues,
//! the event multicasting is performed with scheduler **locked**.
//! However, the scheduler is locked only up to the priority level of
//! the highest-priority subscriber, so any AOs of even higher priority,
//! which did not subscribe to this event are *not* affected.
static void publish_(
QEvt const * const e,
void const * const sender,
std::uint_fast8_t const qs_id) noexcept;
//! Thread routine for executing an active object `act`
static void thread_(QActive * act);
//! Register this active object to be managed by the framework
//!
//! @details
//! This function adds a given active object to the active objects
//! managed by the QF framework. It should not be called by the
//! application directly, only through the function QActive::start().
//!
//! @note
//! The priority of the active object a should be set before calling
//! this function.
//!
//! @sa QActive::unregister_()
void register_() noexcept;
//! Un-register the active object from the framework.
//!
//! @details
//! This function un-registers a given active object from the active objects
//! managed by the QF framework. It should not be called by the QP ports.
//!
//! @param[in] a pointer to the active object to remove from the
//! framework.
//!
//! @note
//! The active object that is removed from the framework can no longer
//! participate in any event exchange.
//!
//! @sa QActive::register_()
void unregister_() noexcept;
#ifdef QF_ISR_API
//! the "FromISR" variant used in the QP port to "FreeRTOS"
virtual bool postFromISR_(
QEvt const * const e,
std::uint_fast16_t const margin,
void * par,
void const * const sender) noexcept;
#endif // def QF_ISR_API
#ifdef QF_ISR_API
//! the "FromISR" variant used in the QP port to "FreeRTOS"
static void publishFromISR_(
QEvt const * e,
void * par,
void const * sender) noexcept;
#endif // def QF_ISR_API
}; // class QActive
} // namespace QP
//$enddecl${QF::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$declare${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
//${QF::QMActive} ............................................................
//! QMActive active object (based on QP::QMsm implementation)
//!
//! @details
//! QP::QMActive represents an active object that uses the QP::QMsm-style
//! state machine implementation strategy. This strategy requires the use of
//! the QM modeling tool to generate state machine code automatically, but
//! the code is faster than in the QP::QHsm-style implementation strategy
//! and needs less run-time support (smaller event-processor).
//!
//! @note
//! QP::QMActive is not intended to be instantiated directly, but rather
//! serves as the base class for derivation of active objects in the
//! applications.
//!
//! @sa QP::QActive
//!
//! @usage
//! The following example illustrates how to derive an active object from
//! QP::QMActive.
//! @include qf_qmactive.cpp
class QMActive : public QP::QActive {
public:
//! inherited from QP::QHsm, but disallowed in QP::QMActive
using QHsm::isIn;
//! inherited from QP::QHsm, but disallowed in QP::QMActive
using QHsm::state;
//! inherited from QP::QHsm, but disallowed in QP::QMActive
using QHsm::childState;
protected:
//! protected constructor (abstract class)
QMActive(QStateHandler const initial) noexcept
: QActive(initial)
{
m_temp.fun = initial;
}
public:
//! delegate to QP::QMsm::init()
void init(
void const * const e,
std::uint_fast8_t const qs_id) override;
//! delegate to QP::QMsm::init()
void init(std::uint_fast8_t const qs_id) override;
//! delegate to QMsm::dispatch()
void dispatch(
QEvt const * const e,
std::uint_fast8_t const qs_id) override;
//! Tests if a given state is part of the active state configuration
bool isInState(QMState const * const st) const noexcept;
//! Return the current active state object (read only)
QMState const * stateObj() const noexcept {
return m_state.obj;
}
//! Return the current active state object (read only)
QMState const * childStateObj(QMState const * const parent) const noexcept;
#ifdef Q_SPY
//! Get the current state handler of the QP::QMsm
QStateHandler getStateHandler() noexcept override;
#endif // def Q_SPY
}; // class QMActive
} // namespace QP
//$enddecl${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$declare${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
//${QF::QTimeEvt} ............................................................
//! Time Event class (inherits QP:QEvt)
//!
//! @details
//! Time events are special QF events equipped with the notion of time
//! passage. The basic usage model of the time events is as follows. An
//! active object allocates one or more QTimeEvt objects (provides the
//! storage for them). When the active object needs to arrange for a timeout,
//! it arms one of its time events to fire either just once (one-shot) or
//! periodically. Each time event times out independently from the others,
//! so a QF application can make multiple parallel timeout requests (from the
//! same or different active objects). When QF detects that the appropriate
//! moment has arrived, it inserts the time event directly into the
//! recipient's event queue. The recipient then processes the time event just
//! like any other event.
//! <br>
//! //! Time events, as any other QF events derive from the QP::QEvt base
//! class. Typically, you will use a time event as-is, but you can also
//! further derive more specialized time events from it by adding some more
//! data members and/or specialized functions that operate on the specialized
//! time events.
//! <br>
//! Internally, the armed time events are organized into a bi-directional
//! linked list. This linked list is scanned in every invocation of the
//! QTimeEvt::tick_() function. Only armed (timing out) time events are in the
//! list, so only armed time events consume CPU cycles.
//!
//! @note
//! QF manages the time events in the macro TICK_X(), which must be called
//! periodically, from the clock tick ISR or from the special QP::QTicker
//! active object.
//!
//! @note
//! Even though QP::QTimeEvt is a subclass of QP::QEvt, QP::QTimeEvt instances
//! can NOT be allocated dynamically from event pools. In other words, it is
//! illegal to allocate QP::QTimeEvt instances with the Q_NEW() or Q_NEW_X()
//! macros.
class QTimeEvt : public QP::QEvt {
private:
//! link to the next time event in the list
QTimeEvt * volatile m_next;
//! the active object that receives the time events
//!
//! @details
//! The m_act pointer is reused inside the QP implementation to hold
//! the head of the list of newly armed time events.
void * m_act;
//! the internal down-counter of the time event
//!
//! @details
//! The down-counter is decremented by 1 in every TICK_X()
//! invocation. The time event fires (gets posted or published) when
//! the down-counter reaches zero.
QTimeEvtCtr volatile m_ctr;
//! the interval for the periodic time event (zero for the one-shot
//! time event)
//!
//! @details
//! The value of the interval is re-loaded to the internal
//! down-counter when the time event expires, so that the time event
//! keeps timing out periodically.
QTimeEvtCtr m_interval;
public:
//! heads of linked lists of time events, one for every clock tick rate
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE];
private:
friend class QXThread;
public:
//! The Time Event constructor
QTimeEvt(
QActive * const act,
enum_t const sgnl,
std::uint_fast8_t const tickRate = 0U);
//! Arm a time event (one shot or periodic) for event posting
//!
//! @details
//! Arms a time event to fire in a specified number of clock ticks and
//! with a specified interval. If the interval is zero, the time event
//! is armed for one shot ('one-shot' time event). The time event gets
//! directly posted (using the FIFO policy) into the event queue of the
//! host active object. After posting, a one-shot time event gets
//! automatically disarmed while a periodic time event (interval != 0)
//! is automatically re-armed.
//!
//! A time event can be disarmed at any time by calling
//! QP::QTimeEvt::disarm(). Also, a time event can be re-armed to fire
//! in a different number of clock ticks by calling QP::QTimeEvt::rearm().
//!
//! @param[in] nTicks number of clock ticks (at the associated rate)
//! to rearm the time event with.
//! @param[in] interval interval (in clock ticks) for periodic time event.
//!
//! @attention
//! Arming an already armed time event is __not__ allowed and is
//! considered a programming error. The QP/C++ framework will assert
//! if it detects an attempt to arm an already armed time event.
//!
//! @usage
//! The following example shows how to arm a one-shot time event from a
//! state machine of an active object:
//! @include qf_state.cpp
void armX(
QTimeEvtCtr const nTicks,
QTimeEvtCtr const interval = 0U) noexcept;
//! Disarm a time event
//!
//! @details
//! Disarm the time event so it can be safely reused.
//!
//! @returns
//! 'true' if the time event was truly disarmed, that is, it was running.
//! The return of 'false' means that the time event was not truly
//! disarmed because it was not running. The 'false' return is only
//! possible for one-shot time events that have been automatically
//! disarmed upon expiration. In that case the 'false' return means that
//! the time event has already been posted or published and should be
//! expected in the active object's state machine.
//!
//! @note
//! there is no harm in disarming an already disarmed time event
bool disarm() noexcept;
//! Rearm a time event
//!
//! @details
//! Rearms a time event with a new number of clock ticks. This function
//! can be used to adjust the current period of a periodic time event
//! or to prevent a one-shot time event from expiring (e.g., a watchdog
//! time event). Rearming a periodic timer leaves the interval unchanged
//! and is a convenient method to adjust the phasing of a periodic
//! time event.
//!
//! @param[in] nTicks number of clock ticks (at the associated rate)
//! to rearm the time event with.
//!
//! @returns
//! 'true' if the time event was running as it was re-armed. The 'false'
//! return means that the time event was not truly rearmed because it was
//! not running. The 'false' return is only possible for one-shot time
//! events that have been automatically disarmed upon expiration. In that
//! case the 'false' return means that the time event has already been
//! posted and should be expected in the active object's state machine.
bool rearm(QTimeEvtCtr const nTicks) noexcept;
//! Check the "was disarmed" status of a time event
//!
//! @details
//! Useful for checking whether a one-shot time event was disarmed in the
//! QTimeEvt_disarm() operation.
//!
//! @returns
//! 'true' if the time event was truly disarmed in the last
//! QTimeEvt::disarm() operation. The 'false' return means that the time
//! event was not truly disarmed, because it was not running at that time.
//! The 'false' return is only possible for one-shot time events that
//! have been automatically disarmed upon expiration. In this case the
//! 'false' return means that the time event has already been posted or
//! published and should be expected in the active object's event queue.
//!
//! @note
//! This function has a **side effect** of setting the "was disarmed"
//! status, which means that the second and subsequent times this
//! function is called the function will return 'true'.
bool wasDisarmed() noexcept;
//! Gets the active object associated with the time event
void const * getAct() const noexcept {
return m_act;
}
//! Gets the current count of the time event
QTimeEvtCtr getCtr() const noexcept {
return m_ctr;
}
//! Gets the interval of the time event
QTimeEvtCtr getInterval() const noexcept {
return m_interval;
}
//! Processes all armed time events at every clock tick
//!
//! @details
//! This function must be called periodically from a time-tick ISR or from
//! a task so that QF can manage the timeout events assigned to the given
//! system clock tick rate.
//!
//! @param[in] tickRate system clock tick rate serviced [1..15].
//! @param[in] sender pointer to a sender object (used in QS only).
//!
//! @attention
//! this function should be called only via the macros TICK_X() or TICK()
//!
//! @note
//! the calls to QTimeEvt::tick_() with different `tickRate` parameter can
//! preempt each other. For example, higher clock tick rates might be
//! serviced from interrupts while others from tasks (active objects).
static void tick_(
std::uint_fast8_t const tickRate,
void const * const sender) noexcept;
#ifdef Q_UTEST
//! Processes one clock tick for QUTest
static void tick1_(
std::uint_fast8_t const tickRate,
void const * const sender);
#endif // def Q_UTEST
#ifdef QF_ISR_API
//! the "FromISR" variant used in the QP port to "FreeRTOS"
static void tickFromISR_(
std::uint_fast8_t const tickRate,
void * par,
void const * sender) noexcept;
#endif // def QF_ISR_API
//! Returns true if all time events are inactive and false
//! any time event is active
//!
//! @details
//! Find out if any time events are armed at the given clock tick rate.
//!
//! @param[in] tickRate system clock tick rate to find out about.
//!
//! @returns
//! 'true' if no time events are armed at the given tick rate and
//! 'false' otherwise.
//!
//! @note
//! This function should be called in critical section.
static bool noActive(std::uint_fast8_t const tickRate) noexcept;
//! encapsulate the cast the m_act attribute to QActive*
QActive * toActive() noexcept {
return static_cast<QActive *>(m_act);
}
//! encapsulate the cast the `QTimeEvt.m_act` attribute
QTimeEvt * toTimeEvt() noexcept {
return static_cast<QTimeEvt *>(m_act);
}
private:
//! private default constructor only for friends
//!
//! @note
//! private default ctor for internal use only
QTimeEvt();
//! private copy constructor to disallow copying of QTimeEvts
QTimeEvt(QTimeEvt const & other) = delete;
//! disallow copying of QP::QTimeEvt
QTimeEvt & operator=(QTimeEvt const & other) = delete;
}; // class QTimeEvt
} // namespace QP
//$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
//${QF::QTicker} .............................................................
//! "Ticker" Active Object class (inherits QP::QActive)
//!
//! @details
//! QP::QTicker is an efficient active object specialized to process
//! QF system clock tick at a specified tick frequency [0..#QF_MAX_TICK_RATE].
//! Placing system clock tick processing in an active object allows you
//! to remove the non-deterministic TICK_X() processing from the interrupt
//! level and move it into the thread-level, where you can prioritize it
//! as low as you wish.
//!
//! @usage
//! The following example illustrates use of QP::QTicker active objects:
//! @include qf_ticker.cpp
class QTicker : public QP::QActive {
public:
//! constructor
explicit QTicker(std::uint_fast8_t const tickRate) noexcept;
void init(
void const * const e,
std::uint_fast8_t const qs_id) override;
void init(std::uint_fast8_t const qs_id) override;
void dispatch(
QEvt const * const e,
std::uint_fast8_t const qs_id) override;
bool post_(
QEvt const * const e,
std::uint_fast16_t const margin,
void const * const sender) noexcept override;
}; // class QTicker
} // namespace QP
//$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$declare${QF::QF-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
namespace QF {
//${QF::QF-base::intNest_} ...................................................
//! Interrupt nesting up-down counter (used in some QF ports)
extern std::uint_fast8_t intNest_;
//${QF::QF-base::init} .......................................................
//! QF initialization
//!
//! @details
//! Initializes QF and must be called exactly once before any other QF
//! function. Typcially, QP::QF::init() is called from main() even before
//! initializing the Board Support Package (BSP).
//!
//! @note
//! QP::QF::init() clears the internal QF variables, so that the framework
//! can start correctly even if the startup code fails to clear the
//! uninitialized data (as is required by the C Standard).
void init();
//${QF::QF-base::stop} .......................................................
//! Function invoked by the application layer to stop the QF
//! application and return control to the OS/Kernel
//!
//! @details
//! This function stops the QF application. After calling this function,
//! QF attempts to gracefully stop the application. This graceful shutdown
//! might take some time to complete. The typical use of this function is
//! for terminating the QF application to return back to the operating
//! system or for handling fatal errors that require shutting down
//! (and possibly re-setting) the system.
//!
//! @attention
//! After calling QF::stop() the application must terminate and cannot
//! continue. In particular, QF::stop() is **not** intended to be followed
//! by a call to QF::init() to "resurrect" the application.
//!
//! @sa QP::QF::onCleanup()
void stop();
//${QF::QF-base::run} ........................................................
//! Transfers control to QF to run the application
//!
//! @details
//! QF::run() is typically called from your startup code after you
//! initialize the QF and start at least one active object with
//! QActive::start().
//!
//! @returns
//! In QK, the QP::QF::run() function does not return.
int_t run();
//${QF::QF-base::onStartup} ..................................................
//! Startup QF callback (defined in applications/ports)
//! @details
//! This callback is invoked right before transferring the control
//! to the QF framework. The purpose is to configure and enable
//! interrupts.
void onStartup();
//${QF::QF-base::onCleanup} ..................................................
//! Cleanup QF callback (defined in applications/ports)
void onCleanup();
//${QF::QF-base::getQueueMin} ................................................
//! This function returns the minimum of free entries of the given
//! event queue of an active object (indicated by priority `prio`)
//!
//! @details
//! Queries the minimum of free ever present in the given event queue of
//! an active object with priority `prio`, since the active object
//! was started.
//!
//! @note
//! QF::getQueueMin() is available only when the native QF event queue
//! implementation is used. Requesting the queue minimum of an unused
//! priority level raises an assertion in the QF. (A priority level
//! becomes used in QF after the call to QActive::register_().)
//!
//! @param[in] prio Priority of the active object, whose queue is queried
//!
//! @returns
//! the minimum of free ever present in the given event queue of an active
//! object with priority `prio`, since the active object was started.
std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio) noexcept;
//${QF::QF-base::psInit} .....................................................
//! Publish-subscribe initialization
//!
//! @deprecated
//! superseded by QActive::psInit()
inline void psInit(
QSubscrList * const subscrSto,
enum_t const maxSignal) noexcept
{
QActive::psInit(subscrSto, maxSignal);
}
//${QF::QF-base::publish_} ...................................................
//! Publish event to all subscribers of a given signal `e->sig`
//!
//! @deprecated
//! superseded by QActive::publish_()
inline void publish_(
QEvt const * const e,
void const * const sender,
std::uint_fast8_t const qs_id) noexcept
{
QActive::publish_(e, sender, qs_id);
}
//${QF::QF-base::tick_} ......................................................
//! Processes all armed time events at every clock tick
//!
//! @deprecated
//! superseded by QTimeEvt::tick_()
inline void tick_(
std::uint_fast8_t const tickRate,
void const * const sender) noexcept
{
QTimeEvt::tick_(tickRate, sender);
}
//${QF::QF-base::NO_MARGIN} ..................................................
//! Special value of margin that causes asserting failure in case
//! event allocation or event posting fails
constexpr std::uint_fast16_t NO_MARGIN {0xFFFFU};
} // namespace QF
} // namespace QP
//$enddecl${QF::QF-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//$declare${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
namespace QP {
namespace QF {
//${QF::QF-dyn::poolInit} ....................................................
//! Event pool initialization for dynamic allocation of events.
//!
//! @details
//! This function initializes one event pool at a time and must be called
//! exactly once for each event pool before the pool can be used.
//!
//! @param[in] poolSto pointer to the storage for the event pool
//! @param[in] poolSize size of the storage for the pool in bytes
//! @param[in] evtSize the block-size of the pool in bytes, which
//! determines the maximum size of events that
//! can be allocated from the pool
//! @note
//! You might initialize many event pools by making many consecutive calls
//! to the QF::poolInit() function. However, for the simplicity of the
//! internal implementation, you must initialize event pools in the
//! ascending order of the event size.
//!
//! @note
//! The actual number of events available in the pool might be actually
//! less than (`poolSize / evtSize`) due to the internal alignment of
//! the blocks that the pool might perform. You can always check the
//! capacity of the pool by calling QF::getPoolMin().
//!
//! @note
//! The dynamic allocation of events is optional, meaning that you might
//! choose not to use dynamic events. In that case calling
//! QF::poolInit() and using up memory for the memory blocks is
//! unnecessary.
//!
//! @sa QF initialization example for QF::init()
void poolInit(
void * const poolSto,
std::uint_fast32_t const poolSize,
std::uint_fast16_t const evtSize) noexcept;
//${QF::QF-dyn::newX_} .......................................................
//! Internal QF implementation of creating new dynamic mutable event
//!
//! @details
//! Allocates an event dynamically from one of the QF event pools.
//!
//! @param[in] evtSize the size (in bytes) of the event to allocate
//! @param[in] margin the number of un-allocated events still available
//! in a given event pool after the allocation
//! completes. The special value QF::NO_MARGIN
//! means that this function will assert if allocation
//! fails.
//! @param[in] sig the signal to be assigned to the allocated event
//!
//! @returns
//! pointer to the newly allocated event. This pointer can be nullptr
//! only if margin!=0 and the event cannot be allocated with the
//! specified margin still available in the given pool.
//!
//! @note
//! The internal QF function QF::newX_() raises an assertion when
//! the margin argument is QF::NO_MARGIN and allocation of the event
//! turns out to be impossible due to event pool depletion, or incorrect
//! (too big) size of the requested event.
//!
//! @note
//! The application code should not call this function directly.
//! The only allowed use is thorough the macros Q_NEW() or Q_NEW_X().
QEvt * newX_(
std::uint_fast16_t const evtSize,
std::uint_fast16_t const margin,
enum_t const sig) noexcept;
//${QF::QF-dyn::gc} ..........................................................
//! Recycle a dynamic event
//!
//! @details
//! This function implements a garbage collector for dynamic events.
//! Only dynamic events are candidates for recycling. (A dynamic event
//! is one that is allocated from an event pool, which is determined as
//! non-zero `e->poolId_` attribute.) Next, the function decrements the
//! reference counter of the event (`e->refCtr_`), and recycles the event
//! only if the counter drops to zero (meaning that no more references
//! are outstanding for this event). The dynamic event is recycled by
//! returning it to the pool from which it was originally allocated.
//!
//! @param[in] e pointer to the event to recycle
//!
//! @note
//! QF invokes the garbage collector at all appropriate contexts, when
//! an event can become garbage (automatic garbage collection), so the
//! application code should have no need to call QF::gc() directly.
//! The QF::gc() function is exposed only for special cases when your
//! application sends dynamic events to the "raw" thread-safe queues
//! (see QP::QEQueue). Such queues are processed outside of QF and the
//! automatic garbage collection is **NOT** performed for these events.
//! In this case you need to call QF::gc() explicitly.
void gc(QEvt const * const e) noexcept;
//${QF::QF-dyn::poolGetMaxBlockSize} .........................................
//! Obtain the block size of any registered event pools
std::uint_fast16_t poolGetMaxBlockSize() noexcept;
//${QF::QF-dyn::newRef_} .....................................................
//! Internal QF implementation of creating new event reference
//!
//! @details
//! Creates and returns a new reference to the current event e
//!
//! @param[in] e pointer to the current event
//! @param[in] evtRef the event reference
//!
//! @returns
//! the newly created reference to the event `e`
//!
//! @note
//! The application code should not call this function directly.
//! The only allowed use is thorough the macro Q_NEW_REF().
QEvt const * newRef_(
QEvt const * const e,
QEvt const * const evtRef) noexcept;
//${QF::QF-dyn::deleteRef_} ..................................................
//! Internal QF implementation of deleting event reference
//!
//! @details
//! Deletes an existing reference to the event e
//!
//! @param[in] evtRef the event reference
//!
//! @note
//! The application code should not call this function directly.
//! The only allowed use is thorough the macro Q_DELETE_REF().
void deleteRef_(QEvt const * const evtRef) noexcept;
//${QF::QF-dyn::getPoolMin} ..................................................
//! This function returns the minimum of free entries of the given
//! event pool
//!
//! @details
//! This function obtains the minimum number of free blocks in the given
//! event pool since this pool has been initialized by a call to
//! QP::QF::poolInit().
//!
//! @param[in] poolId event pool ID in the range 1..QF::maxPool_, where
//! QF::maxPool_ is the number of event pools
//! initialized with the function QF::poolInit().
//! @returns
//! the minimum number of unused blocks in the given event pool.
std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId) noexcept;
//${QF::QF-dyn::newXfromISR_} ................................................
#ifdef QF_ISR_API
//! the "FromISR" variant used in the QP port to "FreeRTOS"
QEvt * newXfromISR_(
std::uint_fast16_t const evtSize,
std::uint_fast16_t const margin,
enum_t const sig) noexcept;
#endif // def QF_ISR_API
//${QF::QF-dyn::gcFromISR} ...................................................
#ifdef QF_ISR_API
//! the "FromISR" variant used in the QP port to "FreeRTOS"
void gcFromISR(QEvt const * e) noexcept;
#endif // def QF_ISR_API
} // namespace QF
} // namespace QP
//$enddecl${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
//============================================================================
// Global namespace...
//$declare${QF-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//${QF-macros::Q_PRIO} .......................................................
//! Create a QP::QPrioSpec object to specify priority of an AO or a thread
#define Q_PRIO(prio_, pthre_) (static_cast<QP::QPrioSpec>((prio_) | (pthre_) << 8U))
//${QF-macros::Q_NEW} ........................................................
#ifndef Q_EVT_CTOR
//! Allocate a dynamic event (case when QP::QEvt is a POD)
//!
//! @details
//! The macro calls the internal QF function QF::newX_() with
//! margin == QF::NO_MARGIN, which causes an assertion when the event
//! cannot be successfully allocated.
//!
//! @param[in] evtT_ event type (class name) of the event to allocate
//! @param[in] sig_ signal to assign to the newly allocated event
//!
//! @returns a valid event pointer cast to the type `evtT_`.
//!
//! @note
//! If #Q_EVT_CTOR is defined, the Q_NEW() macro becomes variadic and
//! takes all the arguments needed by the constructor of the event
//! class being allocated. The constructor is then called by means
//! of the placement-new operator.
//!
//! @usage
//! The following example illustrates dynamic allocation of an event:
//! @include qf_post.cpp
#define Q_NEW(evtT_, sig_) (static_cast<evtT_ *>( \
QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, (sig_))))
#endif // ndef Q_EVT_CTOR
//${QF-macros::Q_NEW} ........................................................
#ifdef Q_EVT_CTOR
//! Allocate a dynamic event (case when QP::QEvt is not a POD)
#define Q_NEW(evtT_, sig_, ...) \
(new(QP::QF::newX_(sizeof(evtT_), QP::QF::NO_MARGIN, 0)) \
evtT_((sig_), ##__VA_ARGS__))
#endif // def Q_EVT_CTOR
//${QF-macros::Q_NEW_X} ......................................................
#ifndef Q_EVT_CTOR
//! Non-asserting allocate a dynamic event (case when QP::QEvt is a POD).
//!
//! @details
//! This macro allocates a new event and sets the pointer `e_`, while
//! leaving at least `margin_` of events still available in the pool
//!
//! @param[out] e_ pointer to the newly allocated event
//! @param[in] evtT_ event type (class name) of the event to allocate
//! @param[in] margin_ number of events that must remain available
//! in the given pool after this allocation. The
//! special value QF::NO_MARGIN causes asserting
//! failure in case event allocation fails.
//! @param[in] sig_ signal to assign to the newly allocated event
//!
//! @returns an event pointer cast to the type `evtT_` or NULL if the
//! event cannot be allocated with the specified `margin`.
//!
//! @note
//! If #Q_EVT_CTOR is defined, the Q_NEW_X() macro becomes variadic and
//! takes all the arguments needed by the constructor of the event
//! class being allocated. The constructor is then called by means
//! of the placement-new operator.
//!
//! @usage
//! The following example illustrates dynamic allocation of an event:
//! @include qf_postx.cpp
#define Q_NEW_X(e_, evtT_, margin_, sig_) \
((e_) = static_cast<evtT_ *>(QP::QF::newX_( \
sizeof(evtT_), (margin_), (sig_))))
#endif // ndef Q_EVT_CTOR
//${QF-macros::Q_NEW_X} ......................................................
#ifdef Q_EVT_CTOR
//! Non-asserting allocate a dynamic event
//! (case when QP::QEvt is not a POD)
#define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \
(e_) = static_cast<evtT_ *>( \
QP::QF::newX_(sizeof(evtT_), (margin_), 0)); \
if ((e_) != nullptr) { \
new((e_)) evtT_((sig_), ##__VA_ARGS__); \
} \
} while (false)
#endif // def Q_EVT_CTOR
//${QF-macros::Q_NEW_REF} ....................................................
//! Create a new reference of the current event `e`
//!
//! @details
//! The current event processed by an active object is available only for
//! the duration of the run-to-completion (RTC) step. After that step, the
//! current event is no longer available and the framework might recycle
//! (garbage-collect) the event. The macro Q_NEW_REF() explicitly creates
//! a new reference to the current event that can be stored and used beyond
//! the current RTC step, until the reference is explicitly recycled by
//! means of the macro Q_DELETE_REF().
//!
//! @param[in,out] evtRef_ event reference to create
//! @param[in] evtT_ event type (class name) of the event reference
//!
//! @usage
//! The example **defer** in the directory `examples/win32/defer` illustrates
//! the use of Q_NEW_REF()
//!
//! @sa Q_DELETE_REF()
#define Q_NEW_REF(evtRef_, evtT_) \
((evtRef_) = static_cast<evtT_ const *>(QP::QF::newRef_(e, (evtRef_))))
//${QF-macros::Q_DELETE_REF} .................................................
//! Delete the event reference
//!
//! @details
//! Every event reference created with the macro Q_NEW_REF() needs to be
//! eventually deleted by means of the macro Q_DELETE_REF() to avoid leaking
//! the event.
//!
//! @param[in,out] evtRef_ event reference to delete
//!
//! @usage
//! The example **defer** in the directory `examples/win32/defer` illustrates
//! the use of Q_DELETE_REF()
//!
//! @sa Q_NEW_REF()
#define Q_DELETE_REF(evtRef_) do { \
QP::QF::deleteRef_((evtRef_)); \
(evtRef_) = 0U; \
} while (false)
//${QF-macros::PUBLISH} ......................................................
#ifdef Q_SPY
//! Invoke the event publishing facility QActive::publish_().
//!
//! @details
//! This macro is the recommended way of publishing events, because it
//! provides the vital information for software tracing and avoids any
//! overhead when the tracing is disabled.
//!
//! @param[in] e_ pointer to the posted event
//! @param[in] e_ pointer to the posted event
//! @param[in] sender_ pointer to the sender object (actually used
//! only when #Q_SPY is defined)
//!
//! @note
//! The pointer to the `sender_` object is not necessarily a pointer
//! to an active object. In fact, if QACTIVE_PUBLISH() is called from an
//! interrupt or other context, you can create a unique object just to
//! unambiguously identify the sender of the event.
//!
//! @sa QActive::publish_()
#define PUBLISH(e_, sender_) \
publish_((e_), (sender_), (sender_)->getPrio())
#endif // def Q_SPY
//${QF-macros::PUBLISH} ......................................................
#ifndef Q_SPY
#define PUBLISH(e_, dummy) publish_((e_), nullptr, 0U)
#endif // ndef Q_SPY
//${QF-macros::POST} .........................................................
#ifdef Q_SPY
//! Invoke the direct event posting facility QActive::post_()
//!
//! @details
//! This macro asserts if the queue overflows and cannot accept the event.
//!
//! @param[in] e_ pointer to the event to post
//! @param[in] sender_ pointer to the sender object.
//!
//! @note
//! The `sendedr_` parameter is actually only used when QS tracing
//! is enabled (macro #Q_SPY is defined). When QS software tracing is
//! disenabled, the POST() macro does not pass the `sender_` parameter,
//1 so the overhead of passing this extra parameter is entirely avoided.
//!
//! @note
//! the pointer to the sender object is not necessarily a pointer to an
//! active object. In fact, if POST() is called from an interrupt or
//! other context, you can create a unique object just to unambiguously
//! identify the sender of the event.
//!
//! @sa QActive::post_()
#define POST(e_, sender_) post_((e_), QP::QF::NO_MARGIN, (sender_))
#endif // def Q_SPY
//${QF-macros::POST} .........................................................
#ifndef Q_SPY
#define POST(e_, dummy) post_((e_), QP::QF::NO_MARGIN, nullptr)
#endif // ndef Q_SPY
//${QF-macros::POST_X} .......................................................
#ifdef Q_SPY
//! Invoke the direct event posting facility QActive::post_()
//! without delivery guarantee
//!
//! @details
//! This macro does not assert if the queue overflows and cannot accept
//! the event with the specified margin of free slots remaining.
//!
//! @param[in] e_ pointer to the event to post
//! @param[in] margin_ the minimum free slots in the queue, which
//! must still be available after posting the event.
//! The special value QF::NO_MARGIN causes
//! asserting failure in case event posting fails.
//! @param[in] sender_ pointer to the sender object.
//!
//! @returns
//! 'true' if the posting succeeded, and 'false' if the posting
//! failed due to insufficient margin of free entries available in
//! the queue.
//!
//! @note
//! The `sender_` parameter is actually only used when QS tracing
//! is enabled (macro #Q_SPY is defined). When QS software tracing is
//! disabled, the POST_X() macro does not pass the `sender_` parameter,
//! so the overhead of passing this extra parameter is entirely avoided.
//!
//! @note
//! The pointer to the sender object is not necessarily a pointer
//! to an active object. In fact, if POST_X() is called from an
//! interrupt or other context, you can create a unique object just to
//! unambiguously identify the sender of the event.
//!
//! @usage
//! @include qf_postx.cpp
#define POST_X(e_, margin_, sender_) \
post_((e_), (margin_), (sender_))
#endif // def Q_SPY
//${QF-macros::POST_X} .......................................................
#ifndef Q_SPY
#define POST_X(e_, margin_, dummy) post_((e_), (margin_), nullptr)
#endif // ndef Q_SPY
//${QF-macros::TICK_X} .......................................................
#ifdef Q_SPY
//! Invoke the system clock tick processing QTimeEvt::tick_()
//!
//! @details
//! This macro is the recommended way of invoking clock tick processing,
//! because it provides the vital information for software tracing and
//! avoids any overhead when the tracing is disabled.
//!
//! @param[in] tickRate_ clock tick rate to be serviced through this call
//! @param[in] sender_ pointer to the sender object. This parameter
//! is actually only used when QS software tracing is enabled
//! (macro #Q_SPY is defined)
//! @note
//! When QS software tracing is disabled, the macro calls
//! QTimeEvt::tick_() without the `sender` parameter, so the overhead
//! of passing this extra parameter is entirely avoided.
//!
//! @note
//! The pointer to the sender object is not necessarily a pointer
//! to an active object. In fact, when TICK_X() is called from
//! an interrupt, you would create a unique object just to unambiguously
//! identify the ISR as the sender of the time events.
//!
//! @sa QTimeEvt::tick_()
#define TICK_X(tickRate_, sender_) tick_((tickRate_), (sender_))
#endif // def Q_SPY
//${QF-macros::TICK_X} .......................................................
#ifndef Q_SPY
#define TICK_X(tickRate_, dummy) tick_((tickRate_), nullptr)
#endif // ndef Q_SPY
//${QF-macros::TICK} .........................................................
//! Invoke the system clock tick processing for rate 0
//! @sa TICK_X()
#define TICK(sender_) TICK_X(0U, (sender_))
//${QF-macros::QF_CRIT_EXIT_NOP} .............................................
#ifndef QF_CRIT_EXIT_NOP
//! No-operation for exiting a critical section
//!
//! @details
//! In some QF ports the critical section exit takes effect only on the
//! next machine instruction. If this next instruction is another entry
//! to a critical section, the critical section won't be really exited,
//! but rather the two adjecent critical sections would be merged.
//! The QF_CRIT_EXIT_NOP() macro contains minimal code required to
//! prevent such merging of critical sections in such merging of
//! critical sections in QF ports, in which it can occur.
#define QF_CRIT_EXIT_NOP() (static_cast<void>(0))
#endif // ndef QF_CRIT_EXIT_NOP
//$enddecl${QF-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#endif // QP_INC_QF_HPP_