qpc/include/qf.h
2023-01-06 12:57:26 -05:00

2048 lines
72 KiB
C

/*$file${include::qf.h} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*
* Model: qpc.qm
* File: ${include::qf.h}
*
* This code has been generated by QM 5.2.4 <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) : qpc
* 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.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*! @file
* @brief QF/C platform-independent public interface.
*/
#ifndef QP_INC_QF_H_
#define QP_INC_QF_H_
/*==========================================================================*/
/*$declare${QF-config} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF-config::QF_MAX_ACTIVE} ..............................................*/
#ifndef QF_MAX_ACTIVE
/*! Maximum number of active objects (configurable value in qf_port.h)
* Valid values: [1U..64U]; default 32U
*/
#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} ...........................................*/
#ifndef QF_MAX_TICK_RATE
/*! Maximum number of clock rates (configurable value in qf_port.h)
* Valid values: [0U..15U]; default 1U
*/
#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} ...............................................*/
#ifndef QF_MAX_EPOOL
/*! Maximum number of event pools (configurable value in qf_port.h)
* 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.
*/
#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} ........................................*/
#ifndef QF_TIMEEVT_CTR_SIZE
/*! Size of the QTimeEvt counter (configurable value in qf_port.h)
* Valid values: 1U, 2U, or 4U; default 4U
*/
#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} ..........................................*/
#ifndef QF_EVENT_SIZ_SIZE
/*! Size of the event-size (configurable value in qf_port.h)
* Valid values: 1U, 2U, or 4U; default 2U
*/
#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} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF-types::QPSetBits} ...................................................*/
#if (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U)
/*! bitmask for the internal representation of QPSet elements */
typedef uint16_t QPSetBits;
#endif /* (8U < QF_MAX_ACTIVE) && (QF_MAX_ACTIVE <= 16U) */
/*${QF-types::QPSetBits} ...................................................*/
#if (16 < QF_MAX_ACTIVE)
typedef uint32_t QPSetBits;
#endif /* (16 < QF_MAX_ACTIVE) */
/*${QF-types::QPSetBits} ...................................................*/
#if (QF_MAX_ACTIVE <= 8U)
typedef uint8_t QPSetBits;
#endif /* (QF_MAX_ACTIVE <= 8U) */
/*${QF-types::QTimeEvtCtr} .................................................*/
#if (QF_TIMEEVT_CTR_SIZE == 2U)
/*! 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.
*/
typedef uint16_t QTimeEvtCtr;
#endif /* (QF_TIMEEVT_CTR_SIZE == 2U) */
/*${QF-types::QTimeEvtCtr} .................................................*/
#if (QF_TIMEEVT_CTR_SIZE == 4U)
typedef uint32_t QTimeEvtCtr;
#endif /* (QF_TIMEEVT_CTR_SIZE == 4U) */
/*${QF-types::QTimeEvtCtr} .................................................*/
#if (QF_TIMEEVT_CTR_SIZE == 1U)
typedef uint8_t QTimeEvtCtr;
#endif /* (QF_TIMEEVT_CTR_SIZE == 1U) */
/*${QF-types::QF_LOG2} .....................................................*/
#ifndef QF_LOG2
/*! Log-base-2 calculation when hardware acceleration
* is NOT provided (#QF_LOG2 not defined).
*/
uint_fast8_t QF_LOG2(QPSetBits x);
#endif /* ndef QF_LOG2 */
/*${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.
*
* ::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 ::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, ::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.
*/
typedef uint16_t QPrioSpec;
/*${QF-types::QSchedStatus} ................................................*/
/*! The scheduler lock status used in some real-time kernels */
typedef uint_fast16_t QSchedStatus;
/*${QF-types::QPSet} .......................................................*/
/*! @brief Priority Set of up to #QF_MAX_ACTIVE elements
* @class QPSet
*
* @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.
*/
typedef struct {
/* public: */
#if (QF_MAX_ACTIVE <= 32)
/*! bitmask with a bit for each element */
QPSetBits volatile bits;
#endif /* (QF_MAX_ACTIVE <= 32) */
#if (32 < QF_MAX_ACTIVE)
/*! bitmasks with a bit for each element */
QPSetBits volatile bits[2];
#endif /* (32 < QF_MAX_ACTIVE) */
} QPSet;
/* public: */
/*! Make the priority set empty */
static inline void QPSet_setEmpty(QPSet * const me) {
#if (QF_MAX_ACTIVE <= 32)
me->bits = 0U;
#else
me->bits[0] = 0U;
me->bits[1] = 0U;
#endif
}
/*! Return 'true' if the priority set is empty */
static inline bool QPSet_isEmpty(QPSet const * const me) {
#if (QF_MAX_ACTIVE <= 32)
return (me->bits == 0U);
#else
return (me->bits[0] == 0U) ? (me->bits[1] == 0U) : false;
#endif
}
/*! Return 'true' if the priority set is NOT empty */
static inline bool QPSet_notEmpty(QPSet const * const me) {
#if (QF_MAX_ACTIVE <= 32)
return (me->bits != 0U);
#else
return (me->bits[0] != 0U) ? true : (me->bits[1] != 0U);
#endif
}
/*! Return 'true' if the priority set has the element n. */
static inline bool QPSet_hasElement(QPSet const * const me,
uint_fast8_t const n)
{
#if (QF_MAX_ACTIVE <= 32U)
return (me->bits & (1U << (n - 1U))) != 0U;
#else
return (n <= 32U)
? ((me->bits[0] & ((uint32_t)1U << (n - 1U))) != 0U)
: ((me->bits[1] & ((uint32_t)1U << (n - 33U))) != 0U);
#endif
}
/*! insert element `n` into the set (n = 1..::QF_MAX_ACTIVE) */
static inline void QPSet_insert(QPSet * const me,
uint_fast8_t const n)
{
#if (QF_MAX_ACTIVE <= 32U)
me->bits = (me->bits | (1U << (n - 1U)));
#else
if (n <= 32U) {
me->bits[0] = (me->bits[0] | ((uint32_t)1U << (n - 1U)));
}
else {
me->bits[1] = (me->bits[1] | ((uint32_t)1U << (n - 33U)));
}
#endif
}
/*! Remove element `n` from the set (n = 1U..::QF_MAX_ACTIVE) */
static inline void QPSet_remove(QPSet * const me,
uint_fast8_t const n)
{
#if (QF_MAX_ACTIVE <= 32U)
me->bits = (me->bits &
(QPSetBits)(~((QPSetBits)1U << (n - 1U))));
#else
if (n <= 32U) {
(me->bits[0] = (me->bits[0] & ~((uint32_t)1U << (n - 1U))));
}
else {
(me->bits[1] = (me->bits[1] & ~((uint32_t)1U << (n - 33U))));
}
#endif
}
/*! Find the maximum element in the set, returns zero if the set is empty */
static inline uint_fast8_t QPSet_findMax(QPSet const * const me) {
#if (QF_MAX_ACTIVE <= 32)
return QF_LOG2(me->bits);
#else
return (me->bits[1] != 0U)
? (QF_LOG2(me->bits[1]) + 32U)
: (QF_LOG2(me->bits[0]));
#endif
}
/*${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 ::QPrioSpec).
*/
typedef QPSet QSubscrList;
/*$enddecl${QF-types} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QActive} ...........................................................*/
/*! @brief Active object class (based on the QHsm implementation strategy)
* @class QActive
* @extends QHsm
*
* @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>
*
* QActive represents an active object that uses the 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 ::QMsm-style implementation
* strategy.
*
* @note
* 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 QMActive
*
* @usage
* The following example illustrates how to derive an active object from
* QActive.
* @include qf_qactive.c
*/
typedef struct QActive {
/* protected: */
QHsm super;
/* private: */
#ifdef QF_EQUEUE_TYPE
/*! OS-dependent event-queue type
* @private @memberof QActive
*
* @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 ::QEQueue.
*/
QF_EQUEUE_TYPE eQueue;
#endif /* def QF_EQUEUE_TYPE */
#ifdef QF_OS_OBJECT_TYPE
/*! OS-dependent per-thread object
* @private @memberof QActive
*
* @details
* This data might be used in various ways, depending on the QF port.
* In some ports me->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 osObject;
#endif /* def QF_OS_OBJECT_TYPE */
#ifdef QF_THREAD_TYPE
/*! OS-dependent representation of the thread of the active object
* @private @memberof QActive
*
* @details
* This data might be used in various ways, depending on the QF port.
* In some ports me->thread is used store the thread handle. In other ports
* me->thread can be a pointer to the Thread-Local-Storage (TLS).
*/
QF_THREAD_TYPE thread;
#endif /* def QF_THREAD_TYPE */
/* public: */
/*! QF-priority [1..#QF_MAX_ACTIVE] of this AO.
* @private @memberof QActive
* @sa ::QPrioSpec
*/
uint8_t prio;
/*! preemption-threshold [1..#QF_MAX_ACTIVE] of this AO.
* @private @memberof QActive
* @sa ::QPrioSpec
*/
uint8_t pthre;
/* private: */
} QActive;
/* protected: */
/*! ::QActive constructor (abstract base class)
* @protected @memberof QActive
*/
void QActive_ctor(QActive * const me,
QStateHandler const initial);
/* private: */
/*! Starts execution of an active object and registers the object
* with the framework
* @private @memberof QActive
*
* @details
* Starts execution of the AO and registers the AO with the framework.
*
* @param[in] prioSpec priority specification for the AO (See ::QPrioSpec)
* @param[in] qSto pointer to the storage for the ring buffer of the
* event queue
* @param[in] qLen length of the event queue [# ::QEvt* pointers]
* @param[in] stkSto pointer to the stack storage (might be NULL)
* @param[in] stkSize stack size [bytes]
* @param[in] par pointer to an extra parameter (might be NULL)
*
* @usage
* The following example shows starting an AO when a per-task stack
* is needed:
* @include qf_start.c
*/
void QActive_start_(QActive * const me,
QPrioSpec const prioSpec,
QEvt const * * const qSto,
uint_fast16_t const qLen,
void * const stkSto,
uint_fast16_t const stkSize,
void const * const par);
/* protected: */
#ifdef QF_ACTIVE_STOP
/*! Stops execution of an active object and removes it from the
* framework's supervision
* @protected @memberof QActive
*
* @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 QActive_stop(QActive * const me);
#endif /* def QF_ACTIVE_STOP */
/* private: */
/*! Posts an event `e` directly to the event queue of the active object
* using the First-In-First-Out (FIFO) policy.
* @private @memberof QActive
*
* @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 differently in various QP/C++
* ports. The provided implementation assumes that the ::QEQueue
* class is used for the ::QActive event queue.
*
* @usage
* @include qf_post.cpp
*
* @sa
* QActive_postLIFO()
*/
bool QActive_post_(QActive * const me,
QEvt const * const e,
uint_fast16_t const margin,
void const * const sender);
/*! Posts an event `e` directly to the event queue of the active object
* using the Last-In-First-Out (LIFO) policy.
* @private @memberof QActive
*
* @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 differently in various QP/C++
* ports. The provided implementation assumes that the ::QEQueue
* class is used for the QActive event queue.
*
* @sa
* QActive_post()
*/
void QActive_postLIFO_(QActive * const me,
QEvt const * const e);
/*! Get an event from the event queue of an active object
* @private @memberof QActive
*
* @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 NULL).
*
* @note
* This function might be implemented differently in various QP/C++
* ports. The provided implementation assumes that the ::QEQueue
* class is used for the QActive event queue.
*/
QEvt const * QActive_get_(QActive * const me);
/* public: */
/*! Subscribes for delivery of signal `sig` to the active object
* @public @memberof QActive
*
* @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 QActive_subscribe(QActive const * const me,
enum_t const sig);
/*! Unsubscribes from the delivery of signal `sig` to the active object
* @public @memberof QActive
*
* @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 QActive_unsubscribe(QActive const * const me,
enum_t const sig);
/*! Unsubscribes from the delivery of all signals to the active object
* @public @memberof QActive
*
* @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 QActive_unsubscribeAll(QActive const * const me);
/*! Publish event to all subscribers of a given signal `e->sig`
* @static @public @memberof QActive
*
* @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.
*/
void QActive_psInit(
QSubscrList * const subscrSto,
enum_t const maxSignal);
/* private: */
/*! Publish event to all subscribers of a given signal `e->sig`
* @static @private @memberof QActive
*
* @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.
*/
void QActive_publish_(
QEvt const * const e,
void const * const sender,
uint_fast8_t const qs_id);
/* protected: */
/*! Defer an event to a given separate event queue
* @protected @memberof QActive
*
* @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(), ::QEQueue, QActive_flushDeferred()
*/
bool QActive_defer(QActive const * const me,
QEQueue * const eq,
QEvt const * const e);
/*! Recall a deferred event from a given event queue
* @protected @memberof QActive
*
* @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_(), ::QEQueue
*/
bool QActive_recall(QActive * const me,
QEQueue * const eq);
/*! Flush the specified deferred queue 'eq'
* @protected @memberof QActive
*
* @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(), ::QEQueue
*/
uint_fast16_t QActive_flushDeferred(QActive const * const me,
QEQueue * const eq);
/* public: */
/*! Generic setting of additional attributes (useful in QP ports)
* @public @memberof QActive
*/
void QActive_setAttr(QActive * const me,
uint32_t attr1,
void const * attr2);
/* private: */
/*! Thread routine for executing an active object `act`
* @private @memberof QActive
*/
void QActive_thread_(QActive * act);
/* protected: */
/*! Register this active object to be managed by the framework
* @protected @memberof QActive
*
* @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 QActive_register_(QActive * const me);
/*! Un-register the active object from the framework
* @protected @memberof QActive
*
* @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] me 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 QActive_unregister_(QActive * const me);
/* private: */
#ifdef QF_ISR_API
/*! the "FromISR" variant used in the QP port to "FreeRTOS"
* @private @memberof QActive
*/
bool QActive_postFromISR_(QActive * const me,
QEvt const * const e,
uint_fast16_t const margin,
void * par,
void const * const sender);
#endif /* def QF_ISR_API */
/* public: */
#ifdef QF_ISR_API
/*! the "FromISR" variant used in the QP port to "FreeRTOS"
* @private @memberof QActive
*/
void QActive_publishFromISR_(
QEvt const * e,
void * par,
void const * sender);
#endif /* def QF_ISR_API */
/*! Internal array of registered active objects
* @static @private @memberof QActive
*/
extern QActive * QActive_active_[QF_MAX_ACTIVE + 1U];
/*! pointer to the array of all subscriber AOs for a given event signal.
* @static @private @memberof QActive
*/
extern QSubscrList * QActive_subscrList_;
/*! The maximum published signal (the size of the subscrList_ array)
* @static @private @memberof QActive
*/
extern enum_t QActive_maxPubSignal_;
/*! Internal array of registered active objects
* @static @private @memberof QActive
*/
extern QActive * QActive_registry_[QF_MAX_ACTIVE + 1U];
/*$enddecl${QF::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QActiveVtable} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QActiveVtable} .....................................................*/
/*! @brief Virtual table for the QActive class */
typedef struct QActiveVtable {
struct QHsmVtable super; /*!< @protected inherits ::QHsmVtable */
/*! @private virtual function to start the AO/thread
* @sa QACTIVE_START()
*/
void (*start)(QActive * const me, QPrioSpec prio,
QEvt const * * const qSto, uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const stkSize,
void const * const par);
/*! @private virtual function to asynchronously post (FIFO)
* an event to the AO
* @sa QACTIVE_POST() and QACTIVE_POST_X()
*/
bool (*post)(QActive * const me, QEvt const * const e,
uint_fast16_t const margin, void const * const sender);
/*! @private virtual function to asynchronously post (LIFO)
* an event to the AO
* @sa QACTIVE_POST_LIFO()
*/
void (*postLIFO)(QActive * const me, QEvt const * const e);
} QActiveVtable;
/*$enddecl${QF::QActiveVtable} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QMActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QMActive} ..........................................................*/
/*! @brief Active object class (based on QMsm implementation strategy)
* @class QMActive
* @extends QActive
*
* @details
* ::QMActive represents an active object that uses the ::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 ::QHsm style implementation strategy and needs
* less run-time support (smaller event-processor).
*
* @note
* ::QMActive is not intended to be instantiated directly, but rather serves
* as the base class for derivation of active objects in the application.
*
* @tr{AQP214}
*
* @usage
* The following example illustrates how to derive an active object from
* ::QMActive. Please note that the ::QActive member @c super is defined as
* the **first** member of the derived struct (see @ref oop).
* @include qf_qmactive.c
*/
typedef struct {
/* protected: */
QActive super;
} QMActive;
/* protected: */
/*! Constructor of ::QMActive class.
* @protected @memberof QMActive
*
* @details
* Performs the first step of active object initialization by assigning
* the virtual pointer and calling the superclass constructor.
*
* @param[in,out] me current instance pointer (see @ref oop)
* @param[in] initial pointer to the event to be dispatched to the MSM
*
* @note Must be called only ONCE before QHSM_INIT().
*
* @sa QHsm_ctor()
*/
void QMActive_ctor(QMActive * const me,
QStateHandler const initial);
/*$enddecl${QF::QMActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QMActiveVtable} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QMActiveVtable} ....................................................*/
/*! @brief Virtual Table for the ::QMActive class (inherited
* from ::QActiveVtable)
*
* @note
* ::QMActive inherits ::QActive exactly, without adding any new virtual
* functions and therefore, ::QMActiveVtable is typedef'ed as ::QActiveVtable.
*/
typedef QActiveVtable QMActiveVtable;
/*$enddecl${QF::QMActiveVtable} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QTimeEvt} ..........................................................*/
/*! @brief Time Event class
* @class QTimeEvt
* @extends 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.
*
* Time events, as any other QF events derive from the ::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.
*
* Internally, the armed time events are organized into linked lists--one
* list for every supported ticking rate. These linked lists are scanned in
* every invocation of the QTIMEEVT_TICK_X() macro. Only armed (timing out)
* time events are in the list, so only armed time events consume CPU cycles.
*
* @sa ::QTimeEvt for the description of the data members
*
* @tr{AQP215}
*
* @note
* QF manages the time events in the QTIMEEVT_TICK_X() macro, which must
* be called periodically, from the clock tick ISR or from other periodic
* source. QTIMEEVT_TICK_X() caYou might also use the special ::QTicker
* active object.
*
* @note
* Even though ::QTimeEvt is a subclass of ::QEvt, ::QTimeEvt instances can NOT
* be allocated dynamically from event pools. In other words, it is illegal to
* allocate ::QTimeEvt instances with the Q_NEW() or Q_NEW_X() macros.
*/
typedef struct QTimeEvt {
/* protected: */
QEvt super;
/* private: */
/*! link to the next time event in the list
* @private @memberof QTimeEvt
*/
struct QTimeEvt * volatile next;
/*! The active object that receives the time events
* @private @memberof QTimeEvt
*/
void * volatile act;
/*! Internal down-counter of the time event.
* @private @memberof QTimeEvt
*
* @details
* The down-counter is decremented by 1 in every QTimeEvt_tick_() call.
* The time event fires (gets posted or published) when the down-counter
* reaches zero.
*/
QTimeEvtCtr volatile ctr;
/*! Interval for periodic time event (zero for one-shot time event)
* @private @memberof QTimeEvt
*
* @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 interval;
/* public: */
} QTimeEvt;
/* public: */
/*! The extended "constructor" to initialize a Time Event.
* @public @memberof QTimeEvt
*
* @details
* When creating a time event, you must commit it to a specific active object
* `act`, tick rate `tickRate` and event signal `sig`. You cannot change
* these attributes later.
*
* @param[in,out] me current instance pointer (see @ref oop)
* @param[in] act pointer to the active object associated with this
* time event. The time event will post itself to this AO.
* @param[in] sig signal to associate with this time event.
* @param[in] tickRate system clock tick rate to associate with this
* time event in the range [0..15].
*
* @note You should call the constructor exactly once for every Time Event
* object **before** arming the Time Event. The ideal place for initializing
* the time event(s) associated with a given AO is the AO's constructor.
*/
void QTimeEvt_ctorX(QTimeEvt * const me,
QActive * const act,
enum_t const sig,
uint_fast8_t const tickRate);
/*! Arm a time event (one shot or periodic) for direct event posting.
* @public @memberof QTimeEvt
*
* @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). When the timeout expires, 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 QTimeEvt_disarm().
* Also, a time event can be re-armed to fire in a different number of clock
* ticks by calling the QTimeEvt_rearm().
*
* @param[in,out] me current instance pointer (see @ref oop)
* @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 periodic time event as well as
* one-shot time event from a state machine of an active object:
* @include qf_tevt.c
*/
void QTimeEvt_armX(QTimeEvt * const me,
QTimeEvtCtr const nTicks,
QTimeEvtCtr const interval);
/*! Disarm a time event.
* @public @memberof QTimeEvt
*
* @details
* Disarm the time event so it can be safely reused.
*
* @param[in,out] me current instance pointer (see @ref oop)
*
* @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 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
* state machine.
*
* @note
* there is no harm in disarming an already disarmed time event
*/
bool QTimeEvt_disarm(QTimeEvt * const me);
/*! Rearm a time event.
* @public @memberof QTimeEvt
*
* @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,out] me current instance pointer (see @ref oop)
* @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 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 state machine.
*/
bool QTimeEvt_rearm(QTimeEvt * const me,
QTimeEvtCtr const nTicks);
/*! Check the "was disarmed" status of a time event.
* @public @memberof QTimeEvt
*
* @details
* Useful for checking whether a one-shot time event was disarmed in the
* QTimeEvt_disarm() operation.
*
* @param[in,out] me current instance pointer (see @ref oop)
*
* @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 QTimeEvt_wasDisarmed(QTimeEvt * const me);
/*! Get the current value of the down-counter of a time event.
* @public @memberof QTimeEvt
*
* @details
* Useful for checking how many clock ticks (at the tick rate associated
* with the time event) remain until the time event expires.
*
* @param[in,out] me current instance pointer (see @ref oop)
*
* @returns
* For an armed time event, the function returns the current value of the
* down-counter of the given time event. If the time event is not armed,
* the function returns 0.
*
* @note
* The function is thread-safe.
*/
QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const * const me);
/*! Processes all armed time events at every clock tick.
* @static @private @memberof QTimeEvt
*
* @details
* This internal helper function processes all armed ::QTimeEvt objects
* associated wit the tick rate `tickRate`.
*
* 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 clock tick rate serviced in this call [1..15].
* @param[in] sender pointer to a sender object (only for QS tracing)
*
* @note
* this function should be called only via the macro QTIMEEVT_TICK_X()
*
* @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).
*
* @sa ::QTimeEvt.
*/
void QTimeEvt_tick_(
uint_fast8_t const tickRate,
void const * const sender);
#ifdef Q_UTEST
/*! Processes one clock tick for QUTest */
void QTimeEvt_tick1_(
uint_fast8_t const tickRate,
void const * const sender);
#endif /* def Q_UTEST */
/*! Returns 'true' if there are no armed time events at a given tick rate.
* @static @public @memberof QTimeEvt
*
* @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.
*/
bool QTimeEvt_noActive(uint_fast8_t const tickRate);
/*! heads of linked lists of time events, one for every clock tick rate */
extern QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE];
/*$enddecl${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QTicker} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QTicker} ...........................................................*/
/*! @brief "Ticker" Active Object class
* @class QTicker
* @extends QActive
*
* @details
* QTicker is an efficient active object specialized to process QF system
* clock tick at a specified tick rate [0..#QF_MAX_TICK_RATE].
* Placing system clock tick processing in an active object allows you
* to remove the non-deterministic QTIMEEVT_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 QTicker active objects:
* @include qf_ticker.c
*/
typedef struct {
/* protected: */
QActive super;
} QTicker;
/* public: */
/*! Constructor of the QTicker Active Object class
* @public @memberof QTicker
*/
void QTicker_ctor(QTicker * const me,
uint_fast8_t const tickRate);
/* private: */
/*! initialization (override)
* @private @memberof QTicker
*/
void QTicker_init_(
QHsm * const me,
void const * const par,
uint_fast8_t const qs_id);
/*! dispatching (override)
* @private @memberof QTicker
*/
void QTicker_dispatch_(
QHsm * const me,
QEvt const * const e,
uint_fast8_t const qs_id);
/* public: */
/*! post (override)
* @private @memberof QTicker
*/
bool QTicker_post_(
QActive * const me,
QEvt const * const e,
uint_fast16_t const margin,
void const * const sender);
/*! post-LIFO (override)
* @private @memberof QTicker
*/
void QTicker_postLIFO_(
QActive * const me,
QEvt const * const e);
/*$enddecl${QF::QTicker} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QF-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QF-base::Attr} .....................................................*/
/*! @brief QF active object framework
* @class QF
*/
typedef struct QF_Attr {
uint8_t dummy; /*< dummy attribute */
} QF;
/*${QF::QF-base::intLock_} .................................................*/
/*! Interrupt lock up-down counter (used in some QF ports )
* @static @private @memberof QF
*/
extern uint_fast8_t volatile QF_intLock_;
/*${QF::QF-base::intNest_} .................................................*/
/*! Interrupt nesting up-down counter (used in some QF ports )
* @static @private @memberof QF
*/
extern uint_fast8_t volatile QF_intNest_;
/*${QF::QF-base::init} .....................................................*/
/*! QF initialization
* @static @public @memberof QF
*
* @details
* Initializes QF and must be called exactly once before any other QF
* function. Typcially, QF_init() is called from main() even before
* initializing the Board Support Package (BSP).
*
* @note
* 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 QF_init(void);
/*${QF::QF-base::stop} .....................................................*/
/*! Function invoked by the application layer to stop the QF
* application and return control to the OS/Kernel.
* @static @public @memberof QF
*
* @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 QF_onCleanup()
*/
void QF_stop(void);
/*${QF::QF-base::run} ......................................................*/
/*! Transfers control to QF to run the application.
* @static @public @memberof QF
*
* @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 QF_run() does not return.
*/
int_t QF_run(void);
/*${QF::QF-base::psInit} ...................................................*/
/*! initialization of publish-subscribe
*
* @deprecated
* @sa QActive_psInit()
*/
static inline void QF_psInit(
QSubscrList * const subscrSto,
enum_t const maxSignal)
{
QActive_psInit(subscrSto, maxSignal);
}
/*${QF::QF-base::getQueueMin} ..............................................*/
/*! This function returns the minimum of free entries of
* the given event queue.
* @static @public @memberof QF
*
* @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
* This function 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 the QActive_register_() function.)
*
* @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.
*/
uint_fast16_t QF_getQueueMin(uint_fast8_t const prio);
/*${QF::QF-base::onStartup} ................................................*/
/*! Startup QF callback.
* @static @public @memberof QF
*
* @details
* The purpose of the QF_onStartup() callback is to configure and enable
* hardware interrupts. The callback is invoked from QF_run(), right before
* starting the underlying real-time kernel. By that time, the application
* is considered ready to receive and service interrupts.
*
* This function is application-specific and is not implemented in QF, but
* rather in the Board Support Package (BSP) for the given application.
*/
void QF_onStartup(void);
/*${QF::QF-base::onCleanup} ................................................*/
/*! Cleanup QF callback.
* @static @public @memberof QF
*
* @details
* QF_onCleanup() is called in some QF ports before QF returns to the
* underlying real-time kernel or operating system.
*
* This function is strongly platform-specific and is not implemented in
* the QF, but either in the QF port or in the Board Support Package (BSP)
* for the given application. Some QF ports might not require implementing
* QF_onCleanup() at all, because many embedded applications don't have
* anything to exit to.
*
* @sa QF_stop()
*/
void QF_onCleanup(void);
/*$enddecl${QF::QF-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QF-dyn} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QF-dyn::poolInit} ..................................................*/
/*! Event pool initialization for dynamic allocation of events.
* @static @public @memberof QF
*
* @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.
*
* @attention
* 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.
*
* Many RTOSes provide fixed block-size heaps, a.k.a. memory pools that can
* be adapted for QF event pools. In case such support is missing, QF provides
* a native QF event pool implementation. The macro #QF_EPOOL_TYPE_ determines
* the type of event pool used by a particular QF port. See structure ::QMPool
* for more information.
*
* @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 QF_poolInit(
void * const poolSto,
uint_fast32_t const poolSize,
uint_fast16_t const evtSize);
/*${QF::QF-dyn::poolGetMaxBlockSize} .......................................*/
/*! Obtain the block size of any registered event pools.
* @static @public @memberof QF
*
* @details
* Obtain the block size of any registered event pools
*/
uint_fast16_t QF_poolGetMaxBlockSize(void);
/*${QF::QF-dyn::getPoolMin} ................................................*/
/*! Obtain the minimum of free entries of the given event pool.
* @static @public @memberof QF
*
* @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 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.
*/
uint_fast16_t QF_getPoolMin(uint_fast8_t const poolId);
/*${QF::QF-dyn::newX_} .....................................................*/
/*! Internal QF implementation of creating new dynamic event.
* @static @private @memberof QF
*
* @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 NULL only if
* margin != #QF_NO_MARGIN 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` parameter 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 * QF_newX_(
uint_fast16_t const evtSize,
uint_fast16_t const margin,
enum_t const sig);
/*${QF::QF-dyn::gc} ........................................................*/
/*! Recycle a dynamic event
* @static @private @memberof QF
*
* @details
* This function implements a simple garbage collector for the 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 ::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 QF_gc(QEvt const * const e);
/*${QF::QF-dyn::newRef_} ...................................................*/
/*! Internal QF implementation of creating new event reference.
* @static @private @memberof QF
*
* @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 * QF_newRef_(
QEvt const * const e,
void const * const evtRef);
/*${QF::QF-dyn::deleteRef_} ................................................*/
/*! Internal QF implementation of deleting event reference.
* @static @private @memberof QF
*
* @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 QF_deleteRef_(void const * const evtRef);
/*$enddecl${QF::QF-dyn} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*$declare${QF::QF-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF::QF-extern-C::onContextSw} ..........................................*/
#ifdef QF_ON_CONTEXT_SW
/*! QF context switch callback used in built-in kernels (QV, QK, QXK)
* @static @public @memberof QF
*
* @details
* This callback function provides a mechanism to perform additional
* custom operations when one of the built-in kernels switches context
* from one thread to another.
*
* @param[in] prev pointer to the previous thread (active object)
* (prev==0 means that `prev` was the idle loop)
* @param[in] next pointer to the next thread (active object)
* (next==0) means that `next` is the idle loop)
* @attention
* QF_onContextSw() is invoked with interrupts **disabled** and must also
* return with interrupts **disabled**.
*
* @note
* This callback is enabled by defining the macro #QF_ON_CONTEXT_SW.
*
* @include qf_oncontextsw.c
*/
void QF_onContextSw(
QActive * prev,
QActive * next);
#endif /* def QF_ON_CONTEXT_SW */
/*$enddecl${QF::QF-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*==========================================================================*/
/*$declare${QF-macros} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
/*${QF-macros::QF_NO_MARGIN} ...............................................*/
/*! Special value of margin that causes asserting failure in case
* event allocation or event posting fails
*/
#define QF_NO_MARGIN ((uint_fast16_t)0xFFFFU)
/*${QF-macros::Q_PRIO} .....................................................*/
/*! Create a ::QPrioSpec object to specify priorty of an AO or a thread */
#define Q_PRIO(prio_, pthre_) ((QPrioSpec)((prio_) | ((pthre_) << 8U)))
/*${QF-macros::Q_NEW} ......................................................*/
#ifndef Q_EVT_CTOR
/*! Allocate a dynamic event (case when ::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.c
*/
#define Q_NEW(evtT_, sig_) ((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \
QF_NO_MARGIN, (enum_t)(sig_)))
#endif /* ndef Q_EVT_CTOR */
/*${QF-macros::Q_NEW} ......................................................*/
#ifdef Q_EVT_CTOR
/*! Asserting allocate a dynamic event
* (case when ::QEvt is not a POD)
*/
#define Q_NEW(evtT_, sig_, ...) \
(evtT_##_ctor((evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \
QF_NO_MARGIN, (sig_)), (enum_t)(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 ::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.c
*/
#define Q_NEW_X(e_, evtT_, margin_, sig_) ((e_) = \
(evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \
(margin_), (enum_t)(sig_)))
#endif /* ndef Q_EVT_CTOR */
/*${QF-macros::Q_NEW_X} ....................................................*/
#ifdef Q_EVT_CTOR
/*! Non-asserting allocate a dynamic event
* (case when ::QEvt is not a POD)
*/
#define Q_NEW_X(e_, evtT_, margin_, sig_, ...) do { \
(e_) = (evtT_ *)QF_newX_((uint_fast16_t)sizeof(evtT_), \
(margin_), (enum_t)(sig_));\
if ((e_) != (evtT_ *)0) { \
evtT_##_ctor((e_), (enum_t)(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_) = (evtT_ const *)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 { \
QF_deleteRef_((evtRef_)); \
(evtRef_) = (void *)0; \
} while (false)
/*${QF-macros::QACTIVE_START} ..............................................*/
/*! Virtual call to start an active object.
*
* @details
* Starts execution of the AO and registers the AO with the framework.
*
* @param[in,out] me_ current instance pointer (see @ref oop)
* @param[in] prioSpec_ priority specification for the Active Object
* @param[in] qSto_ pointer to the storage for the ring buffer of the
* event queue (used only with the built-in ::QEQueue)
* @param[in] qLen_ length of the event queue (in events)
* @param[in] stkSto_ pointer to the stack storage (used only when
* per-AO stack is needed)
* @param[in] stkSize_ stack size (in bytes)
* @param[in] par_ pointer to the additional port-specific parameter(s)
* (might be NULL).
* @usage
* @include qf_start.c
*/
#define QACTIVE_START(me_, prioSpec_, qSto_, qLen_, stkSto_, stkSize_, par_) do { \
Q_ASSERT((Q_HSM_UPCAST(me_))->vptr); \
(*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->start)( \
(QActive *)(me_), (prioSpec_), \
(qSto_), (qLen_), (stkSto_), (stkSize_), (par_)); \
} while (false)
/*${QF-macros::QACTIVE_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,out] me_ current instance pointer (see @ref oop)
* @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 QACTIVE_POST() 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 QACTIVE_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 QACTIVE_POST(me_, e_, sender_) \
((void)(*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)(\
(me_), (e_), QF_NO_MARGIN, (sender_)))
#endif /* def Q_SPY */
/*${QF-macros::QACTIVE_POST} ...............................................*/
#ifndef Q_SPY
#define QACTIVE_POST(me_, e_, dummy) \
((void)(*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)(\
(me_), (e_), QF_NO_MARGIN, (void *)0))
#endif /* ndef Q_SPY */
/*${QF-macros::QACTIVE_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,out] me_ current instance pointer (see @ref oop)
* @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.c
*/
#define QACTIVE_POST_X(me_, e_, margin_, sender_) \
((*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)((me_),\
(e_), (margin_), (sender_)))
#endif /* def Q_SPY */
/*${QF-macros::QACTIVE_POST_X} .............................................*/
#ifndef Q_SPY
#define QACTIVE_POST_X(me_, e_, margin_, dummy) \
((*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->post)((me_),\
(e_), (margin_), (void *)0))
#endif /* ndef Q_SPY */
/*${QF-macros::QACTIVE_POST_LIFO} ..........................................*/
/*! Virtual call to post an event to an active object using the
* Last-In-First-Out (LIFO) policy.
*
* @param[in,out] me_ current instance pointer (see @ref oop)
* @param[in] e_ pointer to the event to post
*/
#define QACTIVE_POST_LIFO(me_, e_) \
((*((QActiveVtable const *)((Q_HSM_UPCAST(me_))->vptr))->postLIFO)( \
(me_), (e_)))
/*${QF-macros::QACTIVE_PUBLISH} ............................................*/
#ifdef Q_SPY
/*! Publish an event to all subscriber Active Objects.
*
* @details
* If #Q_SPY is defined, this macro calls QActive_publish_() with
* the `sender_` parameter to identify the publisher of the event.
* Otherwise, `sender_` is not used.
*
* @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 QACTIVE_PUBLISH(e_, sender_) \
(QActive_publish_((e_), (void const *)(sender_), (sender_)->prio))
#endif /* def Q_SPY */
/*${QF-macros::QACTIVE_PUBLISH} ............................................*/
#ifndef Q_SPY
#define QACTIVE_PUBLISH(e_, dummy) (QActive_publish_((e_), (void *)0, 0U))
#endif /* ndef Q_SPY */
/*${QF-macros::QTIMEEVT_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 QTIMEEVT_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 QTIMEEVT_TICK_X(tickRate_, sender_) \
(QTimeEvt_tick_((tickRate_), (sender_)))
#endif /* def Q_SPY */
/*${QF-macros::QTIMEEVT_TICK_X} ............................................*/
#ifndef Q_SPY
#define QTIMEEVT_TICK_X(tickRate_, dummy) \
(QTimeEvt_tick_((tickRate_), (void *)0))
#endif /* ndef Q_SPY */
/*${QF-macros::QTIMEEVT_TICK} ..............................................*/
/*! Invoke the system clock tick processing
* for tick rate 0
*/
#define QTIMEEVT_TICK(sender_) QTIMEEVT_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() ((void)0)
#endif /* ndef QF_CRIT_EXIT_NOP */
/*${QF-macros::QF_TICK_X} ..................................................*/
/*! Invoke the system clock tick processing
*
* @deprecated
* superseded by QTIMEEVT_TICK_X()
*/
#define QF_TICK_X(tickRate_, sender_) QTIMEEVT_TICK_X((tickRate_), (sender_))
/*${QF-macros::QF_TICK} ....................................................*/
/*! Invoke the system clock tick processing for tick rate 0
*
* @deprecated
* superseded by QTIMEEVT_TICK()
*/
#define QF_TICK(sender_) QTIMEEVT_TICK(sender_)
/*${QF-macros::QF_PUBLISH} .................................................*/
/*! Publish an event to all subscriber Active Objects.
*
* @deprecated
* superseded by QACTIVE_PUBLISH()
*/
#define QF_PUBLISH(e_, sender_) QACTIVE_PUBLISH((e_), (sender_))
/*$enddecl${QF-macros} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#endif /* QP_INC_QF_H_ */