bool QActive::postx_(
QEvt const * const e,
std::uint_fast16_t const margin,
void const * const sender) noexcept // !!! let margin == 1
{
#ifndef Q_SPY
Q_UNUSED_PAR(sender);
#endif
QF_CRIT_STAT
QF_CRIT_ENTRY();
// the event to post must not be NULL
Q_REQUIRE_INCRIT(100, e != nullptr);
// the number of free slots available in the FreeRTOS queue
QEQueueCtr const nFree =
static_cast<QEQueueCtr>(FREERTOS_QUEUE_GET_FREE()); // !!! let nFree == 2
bool status = ((margin == QF::NO_MARGIN)
|| (nFree > static_cast<QEQueueCtr>(margin))); // status == true
if (status) { // should try to post the event?
#if (QF_MAX_EPOOL > 0U)
if (e->poolNum_ != 0U) { // is it a mutable event?
QEvt_refCtr_inc_(e); // increment the reference counter
}
#endif // (QF_MAX_EPOOL > 0U)
// assume that event posting will be successful, see NOTE3
QS_BEGIN_PRE(QS_QF_ACTIVE_POST, m_prio)
QS_TIME_PRE(); // timestamp
QS_OBJ_PRE(sender); // the sender object
QS_SIG_PRE(e->sig); // the signal of the event
QS_OBJ_PRE(this); // this active object (recipient)
QS_2U8_PRE(e->poolNum_, e->refCtr_); // pool-Num & ref-Count
QS_EQC_PRE(nFree); // # free entries available
QS_EQC_PRE(0U); // min # free entries (unknown)
QS_END_PRE()
QF_CRIT_EXIT(); // exit crit.sect. before calling RTOS API
// !!! let another high-priority task also makes postx_ with margin == 1; now, nFree == 1
// post the following evtPtr to the RTOS event queue, see NOTE3
QEvtPtr const evtPtr = {
e
};
status = (xQueueSendToBack(
m_eQueue, static_cast<void const *>(&evtPtr), 0U) == pdPASS); // !!! status == true, nFree == 0, but it should be at least 1 (margin)
QF_CRIT_ENTRY(); // re-enter crit.sec. // !!! enter
}
if (!status) { // event NOT posted?
// posting is allowed to fail only when margin != QF_NO_MARGIN
Q_ASSERT_INCRIT(130, margin != QF::NO_MARGIN);
QS_BEGIN_PRE(QS_QF_ACTIVE_POST_ATTEMPT, m_prio)
QS_TIME_PRE(); // timestamp
QS_OBJ_PRE(sender); // the sender object
QS_SIG_PRE(e->sig); // the signal of the event
QS_OBJ_PRE(this); // this active object (recipient)
QS_2U8_PRE(e->poolNum_, e->refCtr_); // pool-Num & ref-Count
QS_EQC_PRE(nFree); // # free entries
QS_EQC_PRE(margin); // margin requested
QS_END_PRE()
QF_CRIT_EXIT();
#if (QF_MAX_EPOOL > 0U)
QF::gc(e); // recycle the event to avoid a leak
#endif
}
else {
QF_CRIT_EXIT(); // !!! and exit without any work between "enter" and "exit"
}
return status;
}
=================================================================
The problematic areas are marked with comments containing !!!.
Two issues were identified in QActive::postx_:
Incorrect behavior with margin argument under concurrency (race condition)
Unnecessary critical section re-entry causing performance degradation
Problem 1: Margin guarantee violation (race condition)
Summary:
The function checks available queue slots (nFree) inside a critical section, but performs the actual enqueue operation outside of it. This allows another higher-priority task to modify the queue state in between, violating the intended margin guarantee.
Steps to reproduce:
Call postx_() with: margin == 1, Initial nFree == 2
Condition (nFree > margin) evaluates to true, so posting proceeds
Exit critical section (QF_CRIT_EXIT())
Before xQueueSendToBack() executes, another higher-priority task calls postx_() with margin == 1 and consumes one slot: now nFree == 1
Current function resumes and successfully posts to the queue: xQueueSendToBack() returns pdPASS
Queue becomes full (nFree == 0)
Result:
Margin requirement (>= 1 free slot remaining) is violated
Problem 2: Redundant critical section enter/exit (performance issue)
Summary:
There is an unnecessary QF_CRIT_ENTRY() followed immediately by QF_CRIT_EXIT() with no meaningful work in between.
Code path:
After successful enqueue, code re-enters critical section:
QF_CRIT_ENTRY(); // re-enter crit.sec.
Then reaches:
else {
QF_CRIT_EXIT(); // exit without any work between enter and exit
}
Anonymous