/** * \description * The function allocates a memory block from the pool and returns a pointer * to the block back to the caller. * * \arguments * \arg[in,out] \c me pointer (see \ref derivation) * \arg[in] \c margin the minimum number of unused blocks still available * in the pool after the allocation. * * \note This function can be called from any task level or ISR level. * * \note The memory pool \a me must be initialized before any events can * be requested from it. Also, the QMPool_get() function uses internally a * QF critical section, so you should be careful not to call it from within * a critical section when nesting of critical section is not supported. * * \attention * An allocated block must be later returned back to the same pool * from which it has been allocated. * * \sa QMPool_put() */ void *QMPool_get(QMPool * const me, uint_fast16_t const margin) { QFreeBlock *fb; QF_CRIT_STAT_ QF_CRIT_ENTRY_(); /* have more free blocks than the requested margin? */ if (me->nFree > (QMPoolCtr)margin) { void *fb_next; fb = (QFreeBlock *)me->free_head; /* get a free block */ /* the pool has some free blocks, so a free block must be available */ Q_ASSERT_ID(110, fb != (QFreeBlock *)0); fb_next = fb->next; /* put volatile to a temporary to avoid UB */ /* is the pool becoming empty? */ --me->nFree; /* one less free block */ if (me->nFree == (QMPoolCtr)0) { /* pool is becoming empty, so the next free block must be NULL */ Q_ASSERT_ID(120, fb_next == (QFreeBlock *)0); me->nMin = (QMPoolCtr)0; /* remember that the pool got empty */ } else { /* pool is not empty, so the next free block must be in range * * NOTE: the next free block pointer can fall out of range * when the client code writes past the memory block, thus * corrupting the next block. */ Q_ASSERT_ID(130, QF_PTR_RANGE_(fb_next, me->start, me->end)); /* is the number of free blocks the new minimum so far? */ if (me->nMin > me->nFree) { me->nMin = me->nFree; /* remember the new minimum */ } } me->free_head = fb_next; /* set the head to the next free block */ QS_BEGIN_NOCRIT_(QS_QF_MPOOL_GET, QS_priv_.mpObjFilter, me->start) QS_TIME_(); /* timestamp */ QS_OBJ_(me->start); /* the memory managed by this pool */ QS_MPC_(me->nFree); /* # of free blocks in the pool */ QS_MPC_(me->nMin); /* min # free blocks ever in the pool */ QS_END_NOCRIT_() }
/*..........................................................................*/ void QMPool_init(QMPool * const me, void * const poolSto, uint32_t poolSize, QMPoolSize blockSize) { QFreeBlock *fb; uint32_t nblocks; QS_CRIT_STAT_ /* The memory block must be valid * and the poolSize must fit at least one free block * and the blockSize must not be too close to the top of the dynamic range */ Q_REQUIRE((poolSto != (void *)0) && (poolSize >= (uint32_t)sizeof(QFreeBlock)) && ((QMPoolSize)(blockSize + (QMPoolSize)sizeof(QFreeBlock)) > blockSize)); me->free_head = poolSto; /* round up the blockSize to fit an integer # free blocks, no division */ me->blockSize = (QMPoolSize)sizeof(QFreeBlock); /* start with just one */ nblocks = (uint32_t)1; /* # free blocks that fit in one memory block */ while (me->blockSize < blockSize) { me->blockSize += (QMPoolSize)sizeof(QFreeBlock); ++nblocks; } blockSize = me->blockSize; /* use the rounded-up value from now on */ /* the pool buffer must fit at least one rounded-up block */ Q_ASSERT(poolSize >= (uint32_t)blockSize); /* chain all blocks together in a free-list... */ poolSize -= (uint32_t)blockSize; /* don't count the last block */ me->nTot = (QMPoolCtr)1; /* the last block already in the pool */ fb = (QFreeBlock *)me->free_head; /* start at the head of the free list */ while (poolSize >= (uint32_t)blockSize) { fb->next = &QF_PTR_AT_(fb, nblocks);/*point next link to next block */ fb = fb->next; /* advance to the next block */ poolSize -= (uint32_t)blockSize; /* reduce the available pool size */ ++me->nTot; /* increment the number of blocks so far */ } fb->next = (QFreeBlock *)0; /* the last link points to NULL */ me->nFree = me->nTot; /* all blocks are free */ me->nMin = me->nTot; /* the minimum number of free blocks */ me->start = poolSto; /* the original start this pool buffer */ me->end = fb; /* the last block in this pool */ QS_BEGIN_(QS_QF_MPOOL_INIT, QS_priv_.mpObjFilter, me->start) QS_OBJ_(me->start); /* the memory managed by this pool */ QS_MPC_(me->nTot); /* the total number of blocks */ QS_END_() }
/*..........................................................................*/ void *QMPool_get(QMPool *me) { QFreeBlock *fb; QF_INT_LOCK_KEY_ QF_INT_LOCK_(); fb = (QFreeBlock *)me->free; /* get a free block or NULL */ if (fb != (QFreeBlock *)0) { /* free block available? */ me->free = fb->next; /* adjust list head to the next free block */ --me->nFree; /* one less free block */ if (me->nMin > me->nFree) { me->nMin = me->nFree; /* remember the minimum so far */ } } QS_BEGIN_NOLOCK_(QS_QF_MPOOL_GET, QS_mpObj_, me->start) QS_TIME_(); /* timestamp */ QS_OBJ_(me->start); /* the memory managed by this pool */ QS_MPC_(me->nFree); /* the number of free blocks in the pool */ QS_MPC_(me->nMin); /* min number of free blocks ever in the pool */ QS_END_NOLOCK_() QF_INT_UNLOCK_(); return fb; /* return the block or NULL pointer to the caller */ }
/*..........................................................................*/ void QMPool_put(QMPool *me, void *b) { QF_CRIT_STAT_ Q_REQUIRE(me->nFree <= me->nTot); /* # free blocks must be < total */ Q_REQUIRE(QF_PTR_RANGE_(b, me->start, me->end)); /* b must be in range */ QF_CRIT_ENTRY_(); ((QFreeBlock *)b)->next = (QFreeBlock *)me->free_head;/* link into list */ me->free_head = b; /* set as new head of the free list */ ++me->nFree; /* one more free block in this pool */ QS_BEGIN_NOCRIT_(QS_QF_MPOOL_PUT, QS_mpObj_, me->start) QS_TIME_(); /* timestamp */ QS_OBJ_(me->start); /* the memory managed by this pool */ QS_MPC_(me->nFree); /* the number of free blocks in the pool */ QS_END_NOCRIT_() QF_CRIT_EXIT_(); }