qpcpp/include/qf.hpp

906 lines
34 KiB
C++
Raw Normal View History

//============================================================================
// QP/C++ Real-Time Embedded Framework (RTEF)
// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
//
// 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>
// <info@state-machine.com>
//============================================================================
//! @date Last updated on: 2022-04-30
//! @version Last updated for: @ref qpcpp_7_0_0
//!
//! @file
//! @brief QF/C++ platform-independent public interface.
2014-04-13 21:35:34 -04:00
2019-10-27 12:26:31 -04:00
#ifndef QF_HPP
#define QF_HPP
2012-08-14 18:00:48 -04:00
//============================================================================
2019-10-27 12:26:31 -04:00
#ifndef QPSET_HPP
#include "qpset.hpp"
2016-09-29 19:54:50 -04:00
#endif
2012-08-14 18:00:48 -04:00
#ifdef Q_EVT_CTOR
2014-04-13 21:35:34 -04:00
#include <new> // for placement new
#endif // Q_EVT_CTOR
2012-08-14 18:00:48 -04:00
//============================================================================
2012-08-14 18:00:48 -04:00
// apply defaults for all undefined configuration parameters
//
#ifndef QF_EVENT_SIZ_SIZE
2019-10-27 12:26:31 -04:00
//! Default value of the macro configurable value in qf_port.hpp
2020-03-17 21:33:58 -04:00
#define QF_EVENT_SIZ_SIZE 2U
2012-08-14 18:00:48 -04:00
#endif
#ifndef QF_MAX_EPOOL
2019-10-27 12:26:31 -04:00
//! Default value of the macro configurable value in qf_port.hpp
2020-03-17 21:33:58 -04:00
#define QF_MAX_EPOOL 3U
2012-08-14 18:00:48 -04:00
#endif
2013-10-10 20:01:51 -04:00
#ifndef QF_MAX_TICK_RATE
2019-10-27 12:26:31 -04:00
//! Default value of the macro configurable value in qf_port.hpp
2020-03-17 21:33:58 -04:00
//! Valid values: [0U..15U]; default 1U
#define QF_MAX_TICK_RATE 1U
#elif (QF_MAX_TICK_RATE > 15U)
#error "QF_MAX_TICK_RATE exceeds the maximum of 15U"
2013-10-10 20:01:51 -04:00
#endif
2012-08-14 18:00:48 -04:00
#ifndef QF_TIMEEVT_CTR_SIZE
2014-04-13 21:35:34 -04:00
//! macro to override the default QTimeEvtCtr size.
2020-03-17 21:33:58 -04:00
//! Valid values 1U, 2U, or 4U; default 2U
#define QF_TIMEEVT_CTR_SIZE 2U
2012-08-14 18:00:48 -04:00
#endif
//============================================================================
2013-10-10 20:01:51 -04:00
namespace QP {
2012-08-14 18:00:48 -04:00
2020-03-17 21:33:58 -04:00
#if (QF_EVENT_SIZ_SIZE == 1U)
using QEvtSize = std::uint8_t;
#elif (QF_EVENT_SIZ_SIZE == 2U)
2014-04-13 21:35:34 -04:00
//! The data type to store the block-size defined based on
//! the macro #QF_EVENT_SIZ_SIZE.
//! @description
//! The dynamic range of this data type determines the maximum block
//! size that can be managed by the pool.
2020-03-17 21:33:58 -04:00
using QEvtSize = std::uint16_t;
#elif (QF_EVENT_SIZ_SIZE == 4U)
using QEvtSize = std::uint32_t;
2012-08-14 18:00:48 -04:00
#else
2020-03-17 21:33:58 -04:00
#error "QF_EVENT_SIZ_SIZE defined incorrectly, expected 1U, 2U, or 4U"
2012-08-14 18:00:48 -04:00
#endif
//============================================================================
2020-03-17 21:33:58 -04:00
#if (QF_TIMEEVT_CTR_SIZE == 1U)
using QTimeEvtCtr = std::uint8_t;
#elif (QF_TIMEEVT_CTR_SIZE == 2U)
2014-04-13 21:35:34 -04:00
//! type of the Time Event counter, which determines the dynamic
//! range of the time delays measured in clock ticks.
//! @description
//! This alias is configurable via the preprocessor switch
//! #QF_TIMEEVT_CTR_SIZE. The other possible values of this type are
//! as follows: @n
//! std::uint8_t when (QF_TIMEEVT_CTR_SIZE == 1U), and @n
//! std::uint32_t when (QF_TIMEEVT_CTR_SIZE == 4U).
2020-03-17 21:33:58 -04:00
using QTimeEvtCtr = std::uint16_t;
#elif (QF_TIMEEVT_CTR_SIZE == 4U)
using QTimeEvtCtr = std::uint32_t;
2012-08-14 18:00:48 -04:00
#else
2020-03-17 21:33:58 -04:00
#error "QF_TIMEEVT_CTR_SIZE defined incorrectly, expected 1U, 2U, or 4U"
2012-08-14 18:00:48 -04:00
#endif
2014-04-13 21:35:34 -04:00
class QEQueue; // forward declaration
2012-08-14 18:00:48 -04:00
//============================================================================
2016-12-01 10:31:49 -05:00
//! QActive active object (based on QP::QHsm implementation)
//! @description
//! Active objects in QP are encapsulated tasks (each embedding a state
//! machine and an event queue) 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.
//! @n@n
//! 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 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
//!
2016-12-01 10:31:49 -05:00
class QActive : public QHsm {
2016-10-08 12:46:03 -04:00
public: // for access from extern "C" functions
2012-08-14 18:00:48 -04:00
#ifdef QF_EQUEUE_TYPE
2014-04-13 21:35:34 -04:00
//! OS-dependent event-queue type.
//! @description
//! 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.
2012-08-14 18:00:48 -04:00
QF_EQUEUE_TYPE m_eQueue;
#endif
#ifdef QF_OS_OBJECT_TYPE
2014-04-13 21:35:34 -04:00
//! OS-dependent per-thread object.
//! @description
//! 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.
2012-08-14 18:00:48 -04:00
QF_OS_OBJECT_TYPE m_osObject;
#endif
#ifdef QF_THREAD_TYPE
2014-04-13 21:35:34 -04:00
//! OS-dependent representation of the thread of the active object.
//! @description
//! 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).
2012-08-14 18:00:48 -04:00
QF_THREAD_TYPE m_thread;
#endif
2019-10-27 12:26:31 -04:00
#ifdef QXK_HPP // QXK kernel used?
2020-10-01 12:50:17 -04:00
//! QXK dynamic priority (1..#QF_MAX_ACTIVE) of AO/thread.
std::uint8_t m_dynPrio;
2017-08-21 18:21:15 -04:00
#endif
2020-10-01 12:50:17 -04:00
//! QF priority (1..#QF_MAX_ACTIVE) of this active object.
std::uint8_t m_prio;
2015-09-29 11:34:38 -04:00
protected:
//! protected constructor (abstract class)
2020-03-17 21:33:58 -04:00
QActive(QStateHandler const initial) noexcept;
2015-09-29 11:34:38 -04:00
2012-08-14 18:00:48 -04:00
public:
2014-04-13 21:35:34 -04:00
//! Starts execution of an active object and registers the object
//! with the framework.
2020-03-17 21:33:58 -04:00
virtual void start(std::uint_fast8_t const prio,
QEvt const * * const qSto, std::uint_fast16_t const qLen,
void * const stkSto, std::uint_fast16_t const stkSize,
void const * const par);
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! Overloaded start function (no initialization event)
2020-03-17 21:33:58 -04:00
virtual void start(std::uint_fast8_t const prio,
QEvt const * * const qSto, std::uint_fast16_t const qLen,
void * const stkSto, std::uint_fast16_t const stkSize)
2013-10-10 20:01:51 -04:00
{
2020-03-17 21:33:58 -04:00
this->start(prio, qSto, qLen, stkSto, stkSize, nullptr);
2013-10-10 20:01:51 -04:00
}
2012-08-14 18:00:48 -04:00
2019-03-28 11:59:31 -04:00
#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.
2019-03-28 11:59:31 -04:00
void stop(void);
#endif
2012-08-14 18:00:48 -04:00
#ifndef Q_SPY
2015-05-14 16:05:04 -04:00
//! Posts an event @p e directly to the event queue of the active
//! object @p me using the First-In-First-Out (FIFO) policy.
2020-03-17 21:33:58 -04:00
virtual bool post_(QEvt const * const e,
std::uint_fast16_t const margin) noexcept;
2012-08-14 18:00:48 -04:00
#else
2020-03-17 21:33:58 -04:00
virtual bool post_(QEvt const * const e, std::uint_fast16_t const margin,
void const * const sender) noexcept;
2012-08-14 18:00:48 -04:00
#endif
2014-04-13 21:35:34 -04:00
//! Posts an event directly to the event queue of the active object
//! using the Last-In-First-Out (LIFO) policy.
2020-03-17 21:33:58 -04:00
virtual void postLIFO(QEvt const * const e) noexcept;
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! Un-subscribes from the delivery of all signals to the active object.
2020-03-17 21:33:58 -04:00
void unsubscribeAll(void) const noexcept;
2012-08-14 18:00:48 -04:00
2015-05-14 16:05:04 -04:00
//! Subscribes for delivery of signal @p sig to the active object
2020-03-17 21:33:58 -04:00
void subscribe(enum_t const sig) const noexcept;
2012-08-14 18:00:48 -04:00
2015-05-14 16:05:04 -04:00
//! Un-subscribes from the delivery of signal @p sig to the active object.
2020-03-17 21:33:58 -04:00
void unsubscribe(enum_t const sig) const noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Defer an event to a given separate event queue.
2020-03-17 21:33:58 -04:00
bool defer(QEQueue * const eq, QEvt const * const e) const noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Recall a deferred event from a given event queue.
2020-03-17 21:33:58 -04:00
bool recall(QEQueue * const eq) noexcept;
2012-08-14 18:00:48 -04:00
2016-02-10 16:27:39 -05:00
//! Flush the specified deferred queue 'eq'.
2020-03-17 21:33:58 -04:00
std::uint_fast16_t flushDeferred(QEQueue * const eq) const noexcept;
2016-02-10 16:27:39 -05:00
2015-05-14 16:05:04 -04:00
//! Get the priority of the active object.
2020-03-17 21:33:58 -04:00
std::uint_fast8_t getPrio(void) const noexcept {
return static_cast<std::uint_fast8_t>(m_prio);
2015-05-14 16:05:04 -04:00
}
2014-04-13 21:35:34 -04:00
//! Set the priority of the active object.
2020-03-17 21:33:58 -04:00
void setPrio(std::uint_fast8_t const prio) {
m_prio = static_cast<std::uint8_t>(prio);
2013-10-10 20:01:51 -04:00
}
2012-08-14 18:00:48 -04:00
2018-01-10 18:26:05 -05:00
//! Generic setting of additional attributes (useful in QP ports)
2020-03-17 21:33:58 -04:00
void setAttr(std::uint32_t attr1, void const *attr2 = nullptr);
2018-01-10 18:26:05 -05:00
2014-09-22 11:48:11 -04:00
#ifdef QF_OS_OBJECT_TYPE
//! accessor to the OS-object for extern "C" functions, such as
2020-03-17 21:33:58 -04:00
//! the QK or QXK schedulers
QF_OS_OBJECT_TYPE &getOsObject(void) noexcept { return m_osObject; }
2014-09-22 11:48:11 -04:00
#endif
#ifdef QF_THREAD_TYPE
//! accessor to the Thread for extern "C" functions, such as
2020-03-17 21:33:58 -04:00
//! the QK or QXK schedulers
QF_THREAD_TYPE &getThread(void) noexcept { return m_thread; }
2014-09-22 11:48:11 -04:00
#endif
2014-04-13 21:35:34 -04:00
//! Get an event from the event queue of an active object.
2020-03-17 21:33:58 -04:00
QEvt const *get_(void) noexcept;
2012-08-14 18:00:48 -04:00
2018-01-10 18:26:05 -05:00
// duplicated API to be used exclusively inside ISRs (useful in some QP ports)
#ifdef QF_ISR_API
#ifdef Q_SPY
virtual bool postFromISR_(QEvt const * const e,
2020-03-17 21:33:58 -04:00
std::uint_fast16_t const margin, void *par,
void const * const sender) noexcept;
2018-01-10 18:26:05 -05:00
#else
virtual bool postFromISR_(QEvt const * const e,
2020-03-17 21:33:58 -04:00
std::uint_fast16_t const margin, void *par) noexcept;
2018-01-10 18:26:05 -05:00
#endif // Q_SPY
#endif // QF_ISR_API
// friendships...
private:
2012-08-14 18:00:48 -04:00
friend class QF;
friend class QTimeEvt;
2016-12-14 19:07:52 -05:00
friend class QTicker;
2019-10-27 12:26:31 -04:00
#ifdef QK_HPP
2016-05-09 11:44:05 -04:00
friend class QMutex;
2019-10-27 12:26:31 -04:00
#endif // QK_HPP
#ifdef QXK_HPP
2015-12-31 14:56:37 -05:00
friend class QXK;
friend class QXThread;
friend class QXMutex;
friend class QXSemaphore;
2019-10-27 12:26:31 -04:00
#endif // QXK_HPP
2018-10-25 11:13:01 -04:00
#ifdef Q_UTEST
friend class QActiveDummy;
#endif // Q_UTEST
2013-10-10 20:01:51 -04:00
};
2012-08-14 18:00:48 -04:00
//============================================================================
2016-12-01 10:31:49 -05:00
//! QMActive active object (based on QP::QMsm implementation)
//! @description
//! 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
//!
2016-12-01 10:31:49 -05:00
class QMActive : public QActive {
2013-10-10 20:01:51 -04:00
public:
2015-05-14 16:05:04 -04:00
// all the following operations delegate to the QHsm class...
2020-10-01 12:50:17 -04:00
void init(void const * const e,
std::uint_fast8_t const qs_id) override;
2022-05-09 14:52:11 -04:00
void init(std::uint_fast8_t const qs_id) override;
2020-10-01 12:50:17 -04:00
void dispatch(QEvt const * const e,
std::uint_fast8_t const qs_id) override;
2015-05-14 16:05:04 -04:00
2020-12-17 11:39:14 -05:00
#ifdef Q_SPY
//! Get the current state handler of the QMsm
QStateHandler getStateHandler() noexcept override;
#endif
2016-12-01 10:31:49 -05:00
//! Tests if a given state is part of the active state configuration
2020-03-17 21:33:58 -04:00
bool isInState(QMState const * const st) const noexcept;
2016-12-01 10:31:49 -05:00
//! Return the current active state object (read only)
2020-03-17 21:33:58 -04:00
QMState const *stateObj(void) const noexcept {
2016-12-01 10:31:49 -05:00
return m_state.obj;
2016-09-14 12:27:20 -04:00
}
2016-12-01 10:31:49 -05:00
//! Obtain the current active child state of a given parent (read only)
2020-03-17 21:33:58 -04:00
QMState const *childStateObj(QMState const * const parent) const noexcept;
2013-10-10 20:01:51 -04:00
protected:
2015-09-29 11:34:38 -04:00
//! protected constructor (abstract class)
2020-03-17 21:33:58 -04:00
QMActive(QStateHandler const initial) noexcept;
2016-09-29 19:54:50 -04:00
private:
2020-03-17 21:33:58 -04:00
//! operations inherited from QP::QHsm, but disallowed in QP::QMActive
using QHsm::isIn;
using QHsm::state;
using QHsm::childState;
2012-08-14 18:00:48 -04:00
};
//============================================================================
2014-04-13 21:35:34 -04:00
//! Time Event class
//! @description
//! 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.@n
//! @n
//! 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.@n
//! @n
//! Internally, the armed time events are organized into a bi-directional
//! linked list. This linked list is scanned in every invocation of the
//! QP::QF::tickX_() 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.
//!
2012-08-14 18:00:48 -04:00
class QTimeEvt : public QEvt {
private:
2014-04-13 21:35:34 -04:00
//! link to the next time event in the list
2013-10-10 20:01:51 -04:00
QTimeEvt * volatile m_next;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! the active object that receives the time events
//! @description
//! The m_act pointer is reused inside the QP implementation to hold
//! the head of the list of newly armed time events.
2013-10-10 20:01:51 -04:00
void * volatile m_act;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! the internal down-counter of the time event.
//! @description
//! 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.
2013-10-10 20:01:51 -04:00
QTimeEvtCtr volatile m_ctr;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! the interval for the periodic time event (zero for the one-shot
//! time event).
//! @description
//! 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.
2012-08-14 18:00:48 -04:00
QTimeEvtCtr m_interval;
public:
2014-04-13 21:35:34 -04:00
//! The Time Event constructor.
2016-12-01 10:31:49 -05:00
QTimeEvt(QActive * const act, enum_t const sgnl,
2020-03-17 21:33:58 -04:00
std::uint_fast8_t const tickRate = 0U) noexcept;
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! Arm a time event (one shot or periodic) for event posting.
2013-10-10 20:01:51 -04:00
void armX(QTimeEvtCtr const nTicks,
2020-03-17 21:33:58 -04:00
QTimeEvtCtr const interval = 0U) noexcept;
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! Disarm a time event.
2020-03-17 21:33:58 -04:00
bool disarm(void) noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Rearm a time event.
2020-03-17 21:33:58 -04:00
bool rearm(QTimeEvtCtr const nTicks) noexcept;
2012-08-14 18:00:48 -04:00
2018-08-13 11:31:31 -04:00
//! Check the "was disarmed" status of a time event.
2020-03-17 21:33:58 -04:00
bool wasDisarmed(void) noexcept;
2018-08-13 11:31:31 -04:00
2014-04-13 21:35:34 -04:00
//! Get the current value of the down-counter of a time event.
2020-03-17 21:33:58 -04:00
QTimeEvtCtr currCtr(void) const noexcept;
2012-08-14 18:00:48 -04:00
private:
2014-04-13 21:35:34 -04:00
//! private default constructor only for friends
2020-03-17 21:33:58 -04:00
QTimeEvt(void) noexcept;
2014-04-13 21:35:34 -04:00
//! private copy constructor to disallow copying of QTimeEvts
2020-03-17 21:33:58 -04:00
QTimeEvt(QTimeEvt const &) = delete;
2014-04-13 21:35:34 -04:00
//! private assignment operator to disallow assigning of QTimeEvts
2020-03-17 21:33:58 -04:00
QTimeEvt & operator=(QTimeEvt const &) = delete;
2012-08-14 18:00:48 -04:00
2016-12-01 10:31:49 -05:00
//! encapsulate the cast the m_act attribute to QActive*
2020-03-17 21:33:58 -04:00
QActive *toActive(void) noexcept {
return static_cast<QActive *>(m_act);
}
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! encapsulate the cast the m_act attribute to QTimeEvt*
2020-03-17 21:33:58 -04:00
QTimeEvt *toTimeEvt(void) noexcept {
return static_cast<QTimeEvt *>(m_act);
}
2012-08-14 18:00:48 -04:00
friend class QF;
2018-10-25 11:13:01 -04:00
friend class QS;
2019-10-27 12:26:31 -04:00
#ifdef QXK_HPP
2015-12-31 14:56:37 -05:00
friend class QXThread;
2016-09-29 19:54:50 -04:00
friend void QXK_activate_(void);
2019-10-27 12:26:31 -04:00
#endif // QXK_HPP
2012-08-14 18:00:48 -04:00
};
//============================================================================
2016-09-29 19:54:50 -04:00
//! Subscriber List
//! @description
//! 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 priority of an active object.
2020-03-17 21:33:58 -04:00
using QSubscrList = QPSet;
2012-08-14 18:00:48 -04:00
//============================================================================
2014-04-13 21:35:34 -04:00
//! QF services.
//! @description
//! This class groups together QF services. It has only static members and
//! should not be instantiated.
2012-08-14 18:00:48 -04:00
class QF {
public:
2015-05-14 16:05:04 -04:00
//! get the current QF version number string of the form X.Y.Z
static char const *getVersion(void) noexcept {
2015-05-14 16:05:04 -04:00
return versionStr;
}
2014-04-13 21:35:34 -04:00
//! QF initialization.
2012-08-14 18:00:48 -04:00
static void init(void);
2014-04-13 21:35:34 -04:00
//! Publish-subscribe initialization.
2012-08-14 18:00:48 -04:00
static void psInit(QSubscrList * const subscrSto,
2020-03-17 21:33:58 -04:00
enum_t const maxSignal) noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Event pool initialization for dynamic allocation of events.
2020-03-17 21:33:58 -04:00
static void poolInit(void * const poolSto,
std::uint_fast32_t const poolSize,
std::uint_fast16_t const evtSize) noexcept;
2012-08-14 18:00:48 -04:00
2015-09-29 11:34:38 -04:00
//! Obtain the block size of any registered event pools
2020-03-17 21:33:58 -04:00
static std::uint_fast16_t poolGetMaxBlockSize(void) noexcept;
2015-09-29 11:34:38 -04:00
2014-04-13 21:35:34 -04:00
//! Transfers control to QF to run the application.
2013-12-30 17:41:15 -05:00
static int_t run(void);
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Startup QF callback.
2012-08-14 18:00:48 -04:00
static void onStartup(void);
2014-04-13 21:35:34 -04:00
//! Cleanup QF callback.
2012-08-14 18:00:48 -04:00
static void onCleanup(void);
2014-04-13 21:35:34 -04:00
//! Function invoked by the application layer to stop the QF
//! application and return control to the OS/Kernel.
2012-08-14 18:00:48 -04:00
static void stop(void);
2013-10-10 20:01:51 -04:00
#ifndef Q_SPY
2020-03-17 21:33:58 -04:00
static void publish_(QEvt const * const e) noexcept;
static void tickX_(std::uint_fast8_t const tickRate) noexcept;
2013-10-10 20:01:51 -04:00
#else
2014-04-13 21:35:34 -04:00
//! Publish event to the framework.
2020-03-17 21:33:58 -04:00
static void publish_(QEvt const * const e,
2021-04-11 13:25:35 -04:00
void const * const sender,
std::uint_fast8_t const qs_id) noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Processes all armed time events at every clock tick.
2020-03-17 21:33:58 -04:00
static void tickX_(std::uint_fast8_t const tickRate,
void const * const sender) noexcept;
2014-04-13 21:35:34 -04:00
#endif // Q_SPY
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Returns true if all time events are inactive and false
//! any time event is active.
2020-03-17 21:33:58 -04:00
static bool noTimeEvtsActiveX(std::uint_fast8_t const tickRate) noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! This function returns the minimum of free entries of the given
//! event pool.
2020-03-17 21:33:58 -04:00
static std::uint_fast16_t getPoolMin(std::uint_fast8_t const poolId)
noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! This function returns the minimum of free entries of the given
//! event queue.
2020-03-17 21:33:58 -04:00
static std::uint_fast16_t getQueueMin(std::uint_fast8_t const prio)
noexcept;
2012-08-14 18:00:48 -04:00
2018-03-19 14:51:26 -04:00
//! Internal QF implementation of creating new dynamic event.
2020-03-17 21:33:58 -04:00
static QEvt *newX_(std::uint_fast16_t const evtSize,
std::uint_fast16_t const margin,
enum_t const sig) noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Recycle a dynamic event.
2020-03-17 21:33:58 -04:00
static void gc(QEvt const * const e) noexcept;
2012-08-14 18:00:48 -04:00
2018-03-19 14:51:26 -04:00
//! Internal QF implementation of creating new event reference.
2015-09-29 11:34:38 -04:00
static QEvt const *newRef_(QEvt const * const e,
2020-03-17 21:33:58 -04:00
QEvt const * const evtRef) noexcept;
2015-09-29 11:34:38 -04:00
2018-03-19 14:51:26 -04:00
//! Internal QF implementation of deleting event reference.
2020-03-17 21:33:58 -04:00
static void deleteRef_(QEvt const * const evtRef) noexcept;
2018-03-19 14:51:26 -04:00
2014-04-13 21:35:34 -04:00
//! Remove the active object from the framework.
2020-03-17 21:33:58 -04:00
static void remove_(QActive * const a) noexcept;
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! array of registered active objects
2020-03-17 21:33:58 -04:00
static QActive *active_[QF_MAX_ACTIVE + 1U];
2012-08-14 18:00:48 -04:00
2015-05-14 16:05:04 -04:00
//! Thread routine for executing an active object @p act.
2016-12-01 10:31:49 -05:00
static void thread_(QActive *act);
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Register an active object to be managed by the framework
2020-03-17 21:33:58 -04:00
static void add_(QActive * const a) noexcept;
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! Clear a specified region of memory to zero.
2020-03-17 21:33:58 -04:00
static void bzero(void * const start,
std::uint_fast16_t const len) noexcept;
2014-04-13 21:35:34 -04:00
2018-01-10 18:26:05 -05:00
// API to be used exclusively inside ISRs (useful in some QP ports)
#ifdef QF_ISR_API
#ifdef Q_SPY
static void publishFromISR_(QEvt const *e, void *par,
2020-03-17 21:33:58 -04:00
void const *sender) noexcept;
static void tickXfromISR_(std::uint_fast8_t const tickRate, void *par,
void const * const sender) noexcept;
2018-01-10 18:26:05 -05:00
#else
2020-03-17 21:33:58 -04:00
static void publishFromISR_(QEvt const *e, void *par) noexcept;
static void tickXfromISR_(std::uint_fast8_t const tickRate,
void *par) noexcept;
2018-01-10 18:26:05 -05:00
#endif // Q_SPY
2020-03-17 21:33:58 -04:00
static QEvt *newXfromISR_(std::uint_fast16_t const evtSize,
std::uint_fast16_t const margin,
enum_t const sig) noexcept;
static void gcFromISR(QEvt const *e) noexcept;
2018-01-10 18:26:05 -05:00
#endif // QF_ISR_API
2014-04-13 21:35:34 -04:00
// to be used in QF ports only...
private:
//! heads of linked lists of time events, one for every clock tick rate
2013-10-10 20:01:51 -04:00
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE];
2016-12-01 10:31:49 -05:00
friend class QActive;
2013-10-10 20:01:51 -04:00
friend class QTimeEvt;
2018-10-25 11:13:01 -04:00
friend class QS;
2019-10-27 12:26:31 -04:00
#ifdef QXK_HPP
2015-12-31 14:56:37 -05:00
friend class QXThread;
2019-10-27 12:26:31 -04:00
#endif // QXK_HPP
2012-08-14 18:00:48 -04:00
};
2017-06-19 11:42:25 -04:00
//! special value of margin that causes asserting failure in case
//! event allocation or event posting fails
2020-03-17 21:33:58 -04:00
std::uint_fast16_t const QF_NO_MARGIN = 0xFFFFU;
2017-06-19 11:42:25 -04:00
2016-12-14 19:07:52 -05:00
//============================================================================
//! "Ticker" Active Object class
//! @description
//! 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
//!
2016-12-14 19:07:52 -05:00
class QTicker : public QActive {
public:
2020-03-17 21:33:58 -04:00
explicit QTicker(std::uint_fast8_t const tickRate) noexcept; // ctor
2016-12-14 19:07:52 -05:00
2020-10-01 12:50:17 -04:00
void init(void const * const e,
std::uint_fast8_t const qs_id) noexcept override;
2022-05-09 14:52:11 -04:00
void init(std::uint_fast8_t const qs_id) noexcept override;
2020-10-01 12:50:17 -04:00
void dispatch(QEvt const * const e,
std::uint_fast8_t const qs_id) noexcept override;
2016-12-14 19:07:52 -05:00
#ifndef Q_SPY
2020-03-17 21:33:58 -04:00
bool post_(QEvt const * const e,
std::uint_fast16_t const margin) noexcept override;
2016-12-14 19:07:52 -05:00
#else
2020-03-17 21:33:58 -04:00
bool post_(QEvt const * const e, std::uint_fast16_t const margin,
void const * const sender) noexcept override;
2016-12-14 19:07:52 -05:00
#endif
2020-03-17 21:33:58 -04:00
void postLIFO(QEvt const * const e) noexcept override;
2016-12-14 19:07:52 -05:00
};
2014-04-13 21:35:34 -04:00
} // namespace QP
2015-05-14 16:05:04 -04:00
//============================================================================
2013-10-10 20:01:51 -04:00
#ifndef QF_CRIT_EXIT_NOP
2014-04-13 21:35:34 -04:00
//! No-operation for exiting a critical section
//! @description
//! 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.
2020-07-18 17:58:58 -04:00
#define QF_CRIT_EXIT_NOP() (static_cast<void>(0))
2013-10-10 20:01:51 -04:00
#endif
2012-08-14 18:00:48 -04:00
//============================================================================
2014-04-13 21:35:34 -04:00
// Provide the constructor for the QEvt class?
#ifdef Q_EVT_CTOR
2012-08-14 18:00:48 -04:00
2020-03-17 21:33:58 -04:00
#define Q_NEW(evtT_, sig_, ...) \
(new(QP::QF::newX_(sizeof(evtT_), QP::QF_NO_MARGIN, 0)) \
2013-10-10 20:01:51 -04:00
evtT_((sig_), ##__VA_ARGS__))
2020-03-17 21:33:58 -04:00
#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__); \
2013-10-10 20:01:51 -04:00
} \
2019-12-31 15:56:23 -05:00
} while (false)
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
#else // QEvt is a POD (Plain Old Datatype)
2013-10-10 20:01:51 -04:00
2014-04-13 21:35:34 -04:00
//! Allocate a dynamic event.
//! @description
//! The macro calls the internal QF function QP::QF::newX_() with
//! margin == QP::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 @p 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
2020-03-17 21:33:58 -04:00
#define Q_NEW(evtT_, sig_) (static_cast<evtT_ *>( \
QP::QF::newX_(sizeof(evtT_), QP::QF_NO_MARGIN, (sig_))))
2014-04-13 21:35:34 -04:00
//! Allocate a dynamic event (non-asserting version).
//! @description
//! This macro allocates a new event and sets the pointer @p e_, while
//! leaving at least @p 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 QP::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 @p evtT_ or NULL if the
//! event cannot be allocated with the specified @p 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
2020-03-17 21:33:58 -04:00
#define Q_NEW_X(e_, evtT_, margin_, sig_) \
((e_) = static_cast<evtT_ *>(QP::QF::newX_( \
2020-04-03 17:45:32 -04:00
sizeof(evtT_), (margin_), (sig_))))
2012-08-14 18:00:48 -04:00
#endif
2015-09-29 11:34:38 -04:00
//! Create a new reference of the current event `e` */
//! @description
//! 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 refrence
//!
//! @usage
//! The example **defer** in the directory `examples/win32/defer` illustrates
//! the use of Q_NEW_REF()
//!
//! @sa Q_DELETE_REF()
//!
2015-09-29 11:34:38 -04:00
#define Q_NEW_REF(evtRef_, evtT_) \
((evtRef_) = static_cast<evtT_ const *>(QP::QF::newRef_(e, (evtRef_))))
//! Delete the event reference */
//! @description
//! 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()
//!
2015-09-29 11:34:38 -04:00
#define Q_DELETE_REF(evtRef_) do { \
2018-03-19 14:51:26 -04:00
QP::QF::deleteRef_((evtRef_)); \
2020-03-17 21:33:58 -04:00
(evtRef_) = 0U; \
2015-09-29 11:34:38 -04:00
} while (false)
//============================================================================
2012-08-14 18:00:48 -04:00
// QS software tracing integration, only if enabled
2014-04-13 21:35:34 -04:00
#ifdef Q_SPY
//! Invoke the system clock tick processing QP::QF::tickX_().
//! @description
//! 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 QF_tickX_()
//! without the @p 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 QP::QF::tickX_()
2014-04-13 21:35:34 -04:00
#define TICK_X(tickRate_, sender_) tickX_((tickRate_), (sender_))
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Invoke the event publishing facility QP::QF::publish_(). This macro
//! @description
//! 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] sender_ pointer to the sender object. This parameter is
//! actually only used when QS software tracing is enabled
//! (macro #Q_SPY is defined). When QS software tracing is
//! disabled, the macro calls QF_publish_() without the
//! @p 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 QF_PUBLISH() is called from an
//! interrupt or other context, you can create a unique object just to
//! unambiguously identify the publisher of the event.
//!
//! @sa QP::QF::publish_()
2021-04-11 13:25:35 -04:00
#define PUBLISH(e_, sender_) \
publish_((e_), (sender_), (sender_)->getPrio())
2012-08-14 18:00:48 -04:00
2016-12-01 10:31:49 -05:00
//! Invoke the direct event posting facility QP::QActive::post_().
//! @description
//! 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 @p 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 @p 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() is called from an interrupt
//! or other context, you can create a unique object just to
//! unambiguously identify the sender of the event.
//!
//! @sa QP::QActive::post_()
2020-07-18 17:58:58 -04:00
#define POST(e_, sender_) post_((e_), QP::QF_NO_MARGIN, (sender_))
2014-04-13 21:35:34 -04:00
2016-12-01 10:31:49 -05:00
//! Invoke the direct event posting facility QP::QActive::post_()
2014-04-13 21:35:34 -04:00
//! without delivery guarantee.
//! @description
//! 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 QP::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 @p 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 @p 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
2013-10-10 20:01:51 -04:00
#define POST_X(e_, margin_, sender_) \
2013-12-30 17:41:15 -05:00
post_((e_), (margin_), (sender_))
2012-08-14 18:00:48 -04:00
#else
2020-03-17 21:33:58 -04:00
#define PUBLISH(e_, dummy_) publish_((e_))
#define POST(e_, dummy_) post_((e_), QP::QF_NO_MARGIN)
2013-12-30 17:41:15 -05:00
#define POST_X(e_, margin_, dummy_) post_((e_), (margin_))
2014-04-13 21:35:34 -04:00
#define TICK_X(tickRate_, dummy_) tickX_((tickRate_))
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
#endif // Q_SPY
2012-08-14 18:00:48 -04:00
2014-04-13 21:35:34 -04:00
//! Invoke the system clock tick processing for rate 0
//! @sa TICK_X()
2020-03-17 21:33:58 -04:00
#define TICK(sender_) TICK_X(0U, (sender_))
2013-12-30 17:41:15 -05:00
2019-10-27 12:26:31 -04:00
#endif // QF_HPP