mirror of
https://github.com/QuantumLeaps/qpcpp.git
synced 2025-01-28 06:02:56 +08:00
276 lines
11 KiB
C++
276 lines
11 KiB
C++
/// @file
|
|
/// @brief QF/C++ dynamic event management
|
|
/// @cond
|
|
///***************************************************************************
|
|
/// Last updated for version 5.6.5
|
|
/// Last updated on 2016-06-09
|
|
///
|
|
/// Q u a n t u m L e a P s
|
|
/// ---------------------------
|
|
/// innovating embedded systems
|
|
///
|
|
/// Copyright (C) Quantum Leaps, LLC. All rights reserved.
|
|
///
|
|
/// This program is open source software: you can redistribute it and/or
|
|
/// modify it under the terms of the GNU General Public License as published
|
|
/// by the Free Software Foundation, either version 3 of the License, or
|
|
/// (at your option) any later version.
|
|
///
|
|
/// Alternatively, this program may be distributed and modified under the
|
|
/// terms of Quantum Leaps commercial licenses, which expressly supersede
|
|
/// the GNU General Public License and are specifically designed for
|
|
/// licensees interested in retaining the proprietary status of their code.
|
|
///
|
|
/// This program is distributed in the hope that it will be useful,
|
|
/// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
/// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
/// GNU General Public License for more details.
|
|
///
|
|
/// You should have received a copy of the GNU General Public License
|
|
/// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
///
|
|
/// Contact information:
|
|
/// http://www.state-machine.com
|
|
/// mailo:info@state-machine.com
|
|
///***************************************************************************
|
|
/// @endcond
|
|
|
|
#define QP_IMPL // this is QP implementation
|
|
#include "qf_port.h" // QF port
|
|
#include "qf_pkg.h" // QF package-scope interface
|
|
#include "qassert.h" // QP embedded systems-friendly assertions
|
|
#ifdef Q_SPY // QS software tracing enabled?
|
|
#include "qs_port.h" // include QS port
|
|
#else
|
|
#include "qs_dummy.h" // disable the QS software tracing
|
|
#endif // Q_SPY
|
|
|
|
|
|
namespace QP {
|
|
|
|
Q_DEFINE_THIS_MODULE("qf_dyn")
|
|
|
|
// Package-scope objects *****************************************************
|
|
QF_EPOOL_TYPE_ QF_pool_[QF_MAX_EPOOL]; // allocate the event pools
|
|
uint_fast8_t QF_maxPool_; // number of initialized event pools
|
|
|
|
//****************************************************************************
|
|
/// @description
|
|
/// 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.@n
|
|
/// @n
|
|
/// 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
|
|
/// class QP::QMPool for more information.
|
|
///
|
|
/// @param[in] poolSto pointer to the storage for the event pool
|
|
/// @param[in] poolSize size of the storage for the pool in bytes
|
|
/// @param[in] evtSize the block-size of the pool in bytes, which determines
|
|
/// the maximum size of events that can be allocated
|
|
/// from the pool
|
|
/// @note
|
|
/// You might initialize many event pools by making many consecutive calls
|
|
/// to the QF_poolInit() function. However, for the simplicity of the internal
|
|
/// implementation, you must initialize event pools in the ascending order of
|
|
/// the event size.
|
|
///
|
|
/// @note The actual number of events available in the pool might be actually
|
|
/// less than (@p poolSize / @p 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
|
|
/// QP::QF::poolInit() and using up memory for the memory blocks is unnecessary.
|
|
///
|
|
/// @sa QF initialization example for QP::QF::init()
|
|
///
|
|
void QF::poolInit(void * const poolSto,
|
|
uint_fast32_t const poolSize, uint_fast16_t const evtSize)
|
|
{
|
|
/// @pre cannot exceed the number of available memory pools
|
|
Q_REQUIRE_ID(200, QF_maxPool_
|
|
< static_cast<uint_fast8_t>(Q_DIM(QF_pool_)));
|
|
/// @pre please initialize event pools in ascending order of evtSize
|
|
Q_REQUIRE_ID(201, (QF_maxPool_ == static_cast<uint_fast8_t>(0))
|
|
|| (QF_EPOOL_EVENT_SIZE_(
|
|
QF_pool_[QF_maxPool_ - static_cast<uint_fast8_t>(1)])
|
|
< evtSize));
|
|
|
|
QF_EPOOL_INIT_(QF_pool_[QF_maxPool_], poolSto, poolSize, evtSize);
|
|
++QF_maxPool_; // one more pool
|
|
}
|
|
|
|
//****************************************************************************
|
|
/// @description
|
|
/// 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
|
|
/// @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!=0 and the event cannot be allocated with the specified
|
|
/// margin still available in the given pool.
|
|
///
|
|
/// @note The internal QF function QP::QF::newX_() raises an assertion when
|
|
/// the margin argument is 0 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)
|
|
{
|
|
uint_fast8_t idx;
|
|
|
|
// find the pool id that fits the requested event size ...
|
|
for (idx = static_cast<uint_fast8_t>(0); idx < QF_maxPool_; ++idx) {
|
|
if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) {
|
|
break;
|
|
}
|
|
}
|
|
// cannot run out of registered pools
|
|
Q_ASSERT_ID(310, idx < QF_maxPool_);
|
|
|
|
QS_CRIT_STAT_
|
|
QS_BEGIN_(QS_QF_NEW, static_cast<void *>(0), static_cast<void *>(0))
|
|
QS_TIME_(); // timestamp
|
|
QS_EVS_(static_cast<QEvtSize>(evtSize)); // the size of the event
|
|
QS_SIG_(static_cast<QSignal>(sig)); // the signal of the event
|
|
QS_END_()
|
|
|
|
QEvt *e;
|
|
QF_EPOOL_GET_(QF_pool_[idx], e, margin); // get e -- platform-dependent
|
|
|
|
// was e allocated correctly?
|
|
if (e != static_cast<QEvt const *>(0)) {
|
|
e->sig = static_cast<QSignal>(sig); // set the signal
|
|
// store pool ID
|
|
e->poolId_ = static_cast<uint8_t>(
|
|
idx + static_cast<uint_fast8_t>(1));
|
|
// initialize the reference counter to 0
|
|
e->refCtr_ = static_cast<uint8_t>(0);
|
|
}
|
|
else {
|
|
// event was not allocated, assert that the caller provided non-zero
|
|
// margin, which means that they can tollerate bad allocation
|
|
Q_ASSERT_ID(320, margin != static_cast<uint_fast16_t>(0));
|
|
}
|
|
return e;
|
|
}
|
|
|
|
//****************************************************************************
|
|
/// @description
|
|
/// This function implements a simple garbage collector for dynamic events.
|
|
/// Only dynamic events are candidates for recycling. (A dynamic event is one
|
|
/// that is allocated from an event pool, which is determined as non-zero
|
|
/// e->poolId_ attribute.) Next, the function decrements the reference counter
|
|
/// of the event (e->refCtr_), and recycles the event only if the counter
|
|
/// drops to zero (meaning that no more references are outstanding for this
|
|
/// event). The dynamic event is recycled by returning it to the pool from
|
|
/// which it was originally allocated.
|
|
///
|
|
/// @param[in] e pointer to the event to recycle
|
|
///
|
|
/// @note
|
|
/// QF invokes the garbage collector at all appropriate contexts, when
|
|
/// an event can become garbage (automatic garbage collection), so the
|
|
/// application code should have no need to call QP::QF::gc() directly.
|
|
/// The QP::QF::gc() function is exposed only for special cases when your
|
|
/// application sends dynamic events to the "raw" thread-safe queues
|
|
/// (see QP::QEQueue). Such queues are processed outside of QF and the
|
|
/// automatic garbage collection is **NOT** performed for these events.
|
|
/// In this case you need to call QP::QF::gc() explicitly.
|
|
///
|
|
void QF::gc(QEvt const * const e) {
|
|
// is it a dynamic event?
|
|
if (QF_EVT_POOL_ID_(e) != static_cast<uint8_t>(0)) {
|
|
QF_CRIT_STAT_
|
|
QF_CRIT_ENTRY_();
|
|
|
|
// isn't this the last reference?
|
|
if (e->refCtr_ > static_cast<uint8_t>(1)) {
|
|
QF_EVT_REF_CTR_DEC_(e); // decrement the ref counter
|
|
|
|
QS_BEGIN_NOCRIT_(QS_QF_GC_ATTEMPT,
|
|
static_cast<void *>(0), static_cast<void *>(0))
|
|
QS_TIME_(); // timestamp
|
|
QS_SIG_(e->sig); // the signal of the event
|
|
QS_2U8_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt
|
|
QS_END_NOCRIT_()
|
|
|
|
QF_CRIT_EXIT_();
|
|
}
|
|
// this is the last reference to this event, recycle it
|
|
else {
|
|
uint_fast8_t idx = static_cast<uint_fast8_t>(e->poolId_)
|
|
- static_cast<uint_fast8_t>(1);
|
|
|
|
QS_BEGIN_NOCRIT_(QS_QF_GC,
|
|
static_cast<void *>(0), static_cast<void *>(0))
|
|
QS_TIME_(); // timestamp
|
|
QS_SIG_(e->sig); // the signal of the event
|
|
QS_2U8_(e->poolId_, e->refCtr_);// pool Id & refCtr of the evt
|
|
QS_END_NOCRIT_()
|
|
|
|
QF_CRIT_EXIT_();
|
|
|
|
// pool ID must be in range
|
|
Q_ASSERT_ID(410, idx < QF_maxPool_);
|
|
|
|
#ifdef Q_EVT_VIRTUAL
|
|
// explicitly exectute the destructor'
|
|
// NOTE: casting 'const' away is legitimate,
|
|
// because it's a pool event
|
|
QF_EVT_CONST_CAST_(e)->~QEvt(); // xtor,
|
|
#endif
|
|
// cast 'const' away, which is OK, because it's a pool event
|
|
QF_EPOOL_PUT_(QF_pool_[idx], QF_EVT_CONST_CAST_(e));
|
|
}
|
|
}
|
|
}
|
|
|
|
//****************************************************************************
|
|
/// @description
|
|
/// 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, QEvt const * const evtRef) {
|
|
// the provided event reference must not be in use
|
|
Q_REQUIRE_ID(500, evtRef == static_cast<QEvt const *>(0));
|
|
|
|
QF_CRIT_STAT_
|
|
QF_CRIT_ENTRY_();
|
|
// is the current event dynamic?
|
|
if (e->poolId_ != static_cast<uint8_t>(0)) {
|
|
QF_EVT_REF_CTR_INC_(e); // increments the ref counter
|
|
}
|
|
QF_CRIT_EXIT_();
|
|
|
|
return e;
|
|
}
|
|
|
|
//****************************************************************************
|
|
/// @description
|
|
/// Obtain the block size of any registered event pools
|
|
///
|
|
uint_fast16_t QF::poolGetMaxBlockSize(void) {
|
|
return QF_EPOOL_EVENT_SIZE_(
|
|
QF_pool_[QF_maxPool_ - static_cast<uint_fast8_t>(1)]);
|
|
}
|
|
|
|
} // namespace QP
|