mirror of
https://github.com/QuantumLeaps/qpcpp.git
synced 2025-01-14 05:42:57 +08:00
1845 lines
70 KiB
C++
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_
|