µTasker Forum
µTasker Forum => µTasker general => Topic started by: neil on April 13, 2012, 10:38:39 AM
-
Hi Mark,
What happens if there are multiple fnInterruptMessage messages for the same task , are they backed up?
Thanks
Neil
-
Hi Neil
There can be multiple Interrupt Events waiting in a task's queue. You just need to make sure that the task's input queue is defined with enough space to hold the maximum number of such that can be expected.
Regards
Mark
-
I have found that if I send multiple messages, one after the other, only the first message, INITIALIZATION, is recognized:
fnInterruptMessage(TASK_ALT_TEMP_DRIVER, INITIALIZATION);
fnInterruptMessage(TASK_ALT_TEMP_DRIVER, EV_ALARM_CONTROLLER_INITIALIZED);
I have looked through the uTasker documentation and cannot locate how to define a message queue large enough so that the second message, EV_ALARM_CONTROLLER_INITIALIZED, can be handled. The task definition with the queue size is:
{"7_alt_temp_Driver",fnAltitudeTempDriver, LARGE_QUE,(DELAY_LIMIT)(NO_DELAY_RESERVE_MONO), 0, UTASKER_STOP}
I have noticed if that if the first message is sent and handled, then the second, both will wake the task, TASK_ALT_TEMP_DRIVER, and be read and handled correctly.
Any help would be appreciated. Thank you.
-
I have figured out what is happening. uTasker will activate the task each time a new message is created. If it happens that several messages are sent right after each other, only one of the activations for the task will occur. To work around this, I read out all the messages sent into a two dimensional ucInputMessage array:
// read out all the messages to be handled in this session
numOfMessagesToHandle = 0;
while(fnRead(PortIDInternal, ucInputMessage[numOfMessagesToHandle], HEADER_LENGTH))
{
if(++numOfMessagesToHandle > MAX_NUM_MSG_TO_HANDLE_FOR_TASK_ALT_TEMP_DRIVER)
{
errorCode.uTaskerTooManyAltTempMessages = TRUE;
break;
}
}
After this, the code handles each of the messages in the ucInputMessage[][] array:
for(index = 0; index < numOfMessagesToHandle; index++)
{
.....
}
This solution will allow a task to handle all messages regardless of when they were sent allowing for the task to complete even if messages and timeouts are created within the handling task itself. The issue with using a while loop on reading its message and handling it one at a time is it is possible for the handling task to create new messages to itself thereby not allowing the other tasks in uTasker operating system to have CPU time. By leaving the handling task, other tasks will get CPU time and the handling task will be activated sometime after the other tasks have had CPU time.
-
Hi
>>{"7_alt_temp_Driver",fnAltitudeTempDriver, LARGE_QUE,(DELAY_LIMIT)(NO_DELAY_RESERVE_MONO), 0, UTASKER_STOP}
LARGE_QUE defines the input queue size of this task. An interrupt event message typically occupies 5 bytes so the max. number of queued interrupt events can be calculated by dividing the queue length by 5.
If losing an event due to lack of queue space were to be critical the queue space available can be checked before sending an event (and it can be attempted later - by whatever mechanism is needed to handle this suitably).
It is correct that interrupt events "schedule" the destination task, and sending multiple messages in a row will cause them all to be queued but the task scheduled just the once. This is why they are usually read out in a while loop so that all waiting events are handled.
If your task sends events to itself within the queue read loop they will also normally be handled in the same loop, before the task yields.
This behavior can however also be controlled if you prefer to quit the task in-between by doing the following (for example)
while (fnRead(PortIDInternal, ucInputMessage, HEADER_LENGTH) != 0) { // check task input queue
....
fnInterruptMessage(OWN_TASK, MY_EVENT); // post an event to our self
return; // yield so that other tasks can run - we will be scheduled again due to the interrupt event post
....
}
Another technique that you could use is to count the number of events you want to handle, as you are doing, and yield when you don't want to handle any more by doing the following:
// read out all the messages to be handled in this session
numOfMessagesToHandle = 0;
while(fnRead(PortIDInternal, ucInputMessage[numOfMessagesToHandle], HEADER_LENGTH))
{
if(++numOfMessagesToHandle > MAX_NUM_MSG_TO_HANDLE_FOR_TASK_ALT_TEMP_DRIVER)
{
uTaskerStateChange(OWN_TASK, UTASKER_ACTIVATE); // yield but unconditionally schedule the task again in case there are still waiting events to be handled
return;
}
}
Notice that here the task is re-scheduled to ensure that if there iare further events waiting in the input queue they will subsequently be handled. If there is nothing it will check once (to be sure) and quit.
If you look at the TCP / Ethernet task it does this when ETHERNET_RELEASE_AFTER_EVERY_FRAME is enabled (handles only ever one single frame at a time) or ETHERNET_RELEASE_LIMIT enabled (handles up to max. this number of frames). This is important for the Ethernet task since there can be fast broadcasts being received on the network which otherwise could cause it to handle a lot of frames before yielding (and starving other tasks). Also if there were a DoS attack (Denial of Service) this ensures that it doesn't spend all its time working on clearing these frames and again blocking other task operations.
Regards
Mark
-
Thank you, Mark, for providing an alternate way to handle this situation. I will take your advice and incorporate the changes in my code.
Regards,
Tom