I’m working implementing “RPC” over MQTT, but I’m having trouble working out how to implement timeouts safely.
Currently I have an MQTT task which reads commands from a queue, but I need to return responses to these commands to the caller. The current way I’m doing this is by pushing items like this to the queue:
typedef struct xQUEUE_ITEM
{
// The handle of the task that pushed this item to the queue.
TaskHandle_t xTaskHandle;
// The response from the task.
void * pvResponse;
} QueueItem_t;
And then using xTaskNotifyWait
(in the caller) and xTaskNotify
(in the MQTT task) to notify the caller that a response has been written by the task.
Here’s a stripped-down example of what I am doing:
static void prvMQTTTask( void * pvArg )
{
for( ; ; )
{
QueueItem_t xItem;
if( xQueueReceive( xQueue, &( xItem ), portMAX_DELAY ) == pdTRUE ) {
strcpy( xItem.pvResponse, "This is an example!" );
xTaskNotify( xItem.xTaskHandle, 0, eSetValueWithOverwrite );
}
}
}
static BaseType_t xMQTTSend( void * pvResponse, TickType_t xTicksToWait )
{
// Clear any pending notifications.
xTaskNotifyWait( 0, 0, NULL, 0 );
BaseType_t xResult = pdPASS;
const QueueItem_t xItem = {
.xTaskHandle = xTaskGetCurrentTaskHandle(),
.pvResponse = pvResponse,
};
const TickType_t xStartTick = xTaskGetTickCount();
if( xQueueSendToBack( xQueue, &( xItem ), xTicksToWait ) == errQUEUE_FULL )
{
xResult = pdFAIL;
}
if( xResult == pdPASS )
{
const TickType_t xEnqueueTicks = xTaskGetTickCount() - xStartTick;
if( xEnqueueTicks > xTicksToWait )
{
// Already timed out.
xResult = pdFAIL;
}
else
{
// Subtract time spent enqueueing.
xTicksToWait -= xEnqueueTicks;
}
}
if ( xResult == pdPASS )
{
// Wait for acknowledgement.
if ( xTaskNotifyWait( 0, 0, NULL, xTicksToWait ) == pdFAIL )
{
// Timed out.
xResult = pdFAIL;
}
}
return xResult;
}
The issue I’m having with timeouts is that the MQTT task could end up writing to xItem.pvResponse
after it has already “expired” (i.e. after xTaskNotifyWait
has returned), which could end up writing to a variable that no longer exists.
Has anyone got any ideas on this? I think I need a way to notify the MQTT task that the item was cancelled.