mirror of
https://github.com/QuantumLeaps/qpc.git
synced 2025-01-14 06:43:19 +08:00
251 lines
9.8 KiB
C
251 lines
9.8 KiB
C
/**
|
|
* @file
|
|
* @brief Dynamic event management
|
|
* @ingroup qf
|
|
* @cond
|
|
******************************************************************************
|
|
* Last updated for version 5.4.0
|
|
* Last updated on 2015-04-25
|
|
*
|
|
* Q u a n t u m L e a P s
|
|
* ---------------------------
|
|
* innovating embedded systems
|
|
*
|
|
* Copyright (C) Quantum Leaps, www.state-machine.com.
|
|
*
|
|
* 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:
|
|
* Web: www.state-machine.com
|
|
* Email: 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 */
|
|
|
|
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 */
|
|
|
|
/****************************************************************************/
|
|
#ifdef Q_EVT_CTOR /* Provide the constructor for the ::QEvt class? */
|
|
|
|
/**
|
|
* @description
|
|
* Constructor for the ::QEvt class provided when the switch #Q_EVT_CTOR
|
|
* is defined.
|
|
*
|
|
* @param[in,out] me pointer (see @ref oop)
|
|
* @param[in] sig signal to be assigned to the event
|
|
*/
|
|
QEvt *QEvt_ctor(QEvt * const me, enum_t const sig) {
|
|
/** @pre the me pointer must be valid */
|
|
Q_REQUIRE_ID(100, me != (QEvt *)0);
|
|
me->sig = (QSignal)sig;
|
|
return me;
|
|
}
|
|
|
|
#endif /* Q_EVT_CTOR */
|
|
|
|
|
|
/****************************************************************************/
|
|
/**
|
|
* @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.
|
|
*
|
|
* @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 (@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 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)
|
|
{
|
|
/** @pre cannot exceed the number of available memory pools */
|
|
Q_REQUIRE_ID(200, QF_maxPool_ < (uint_fast8_t)Q_DIM(QF_pool_));
|
|
/** @pre please initialize event pools in ascending order of evtSize: */
|
|
Q_REQUIRE_ID(201, (QF_maxPool_ == (uint_fast8_t)0)
|
|
|| (QF_EPOOL_EVENT_SIZE_(QF_pool_[QF_maxPool_ - (uint_fast8_t)1])
|
|
< evtSize));
|
|
|
|
/* perform the platform-dependent initialization of the pool */
|
|
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 QF_newX_() raises an assertion when
|
|
* the @p margin parameter 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)
|
|
{
|
|
QEvt *e;
|
|
uint_fast8_t idx;
|
|
QS_CRIT_STAT_
|
|
|
|
/* find the pool index that fits the requested event size ... */
|
|
for (idx = (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_BEGIN_(QS_QF_NEW, (void *)0, (void *)0)
|
|
QS_TIME_(); /* timestamp */
|
|
QS_EVS_(evtSize); /* the size of the event */
|
|
QS_SIG_((QSignal)sig); /* the signal of the event */
|
|
QS_END_()
|
|
|
|
QF_EPOOL_GET_(QF_pool_[idx], e, margin); /* get e -- platform-dependent */
|
|
|
|
/* was e allocated correctly? */
|
|
if (e != (QEvt *)0) {
|
|
e->sig = (QSignal)sig; /* set signal for this event */
|
|
e->poolId_ = (uint8_t)(idx + (uint_fast8_t)1); /* store the pool ID */
|
|
e->refCtr_ = (uint8_t)0; /* set the reference counter to 0 */
|
|
}
|
|
/* event cannot be allocated */
|
|
else {
|
|
/* must tolerate bad alloc. */
|
|
Q_ASSERT_ID(320, margin != (uint_fast16_t)0);
|
|
}
|
|
return e; /* can't be NULL if we can't tolerate bad allocation */
|
|
}
|
|
|
|
/****************************************************************************/
|
|
/**
|
|
* @description
|
|
* 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) {
|
|
|
|
/* is it a dynamic event? */
|
|
if (e->poolId_ != (uint8_t)0) {
|
|
QF_CRIT_STAT_
|
|
QF_CRIT_ENTRY_();
|
|
|
|
/* isn't this the last ref? */
|
|
if (e->refCtr_ > (uint8_t)1) {
|
|
QF_EVT_REF_CTR_DEC_(e); /* decrements the ref counter */
|
|
|
|
QS_BEGIN_NOCRIT_(QS_QF_GC_ATTEMPT, (void *)0, (void *)0)
|
|
QS_TIME_(); /* timestamp */
|
|
QS_SIG_(e->sig); /* the signal of the event */
|
|
QS_2U8_(e->poolId_, e->refCtr_); /* pool Id & ref Count */
|
|
QS_END_NOCRIT_()
|
|
|
|
QF_CRIT_EXIT_();
|
|
}
|
|
/* this is the last reference to this event, recycle it */
|
|
else {
|
|
uint_fast8_t idx = (uint_fast8_t)e->poolId_ - (uint_fast8_t)1;
|
|
|
|
QS_BEGIN_NOCRIT_(QS_QF_GC, (void *)0, (void *)0)
|
|
QS_TIME_(); /* timestamp */
|
|
QS_SIG_(e->sig); /* the signal of the event */
|
|
QS_2U8_(e->poolId_, e->refCtr_); /* pool Id & ref Count */
|
|
QS_END_NOCRIT_()
|
|
|
|
QF_CRIT_EXIT_();
|
|
|
|
/* pool ID must be in range */
|
|
Q_ASSERT_ID(410, idx < QF_maxPool_);
|
|
|
|
/* casting const away is legitimate, because it's a pool event */
|
|
QF_EPOOL_PUT_(QF_pool_[idx], (QEvt *)e);
|
|
}
|
|
}
|
|
}
|
|
|