/** * @file * @brief Dynamic event management * @ingroup qf * @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 . * * Contact information: * http://www.state-machine.com * mailto: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); } } } /****************************************************************************/ /** * @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) { QF_CRIT_STAT_ /* the provided event reference must not be in use */ Q_REQUIRE_ID(500, evtRef == (QEvt const *)0); QF_CRIT_ENTRY_(); /* is the current event dynamic? */ if (e->poolId_ != (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_ - (uint_fast8_t)1]); }