08-21-2005 10:38 AM
I have a program that has a timer callback that calls a function to update some display indicators by calling a UUT communication routine. Separate command buttons can also execute UUT commands over the same communication interface. The issue I have is how to block the execution of the communication routine by the command buttons if the timer callback is already active until the timer callback completes. While the UUT communication routine normally completes extremely quickly, it can at some intervals take up to 4 or 5 seconds to get a response. To prevent locking out the user interface, the communication routine makes calls to ProcessSystemEvents while it is waiting. This of course allows the user command button event to interrupt the timer callback.
I was thinking about letting the command button callback disable the timer, then wait for the timer callback to complete before the button callback can access the communication routine. If this was a multithreaded application I would have been able to lock and block thread access, but this program has everything in a single thread. Is there a simple way to pass control back to the timer callback and once it has completed to resume processing in the button callback?
08-21-2005 12:29 PM
08-21-2005 01:31 PM - edited 08-21-2005 01:31 PM
Message Edited by mvr on 08-21-2005 01:32 PM
08-22-2005 07:23 AM - edited 08-22-2005 07:23 AM
I suppose you could use PostDeferredCall() within the button callback so that it keeps getting called until the timer has finished.
Let's suppose 'inTimer' is a flag you set to tell you if you are currently in the timer routine, then as a very simplistic example:
void CVICALLBACK DeferredCommandButton(void *callbackData)
{
// Need to call the callback again here
CommandButtonStuff(myPanel, PANEL_COMMANDBUTTON, EVENT_COMMIT, NULL, 0, 0);
}
int CVICALLBACK CommandButtonStuff (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
switch (event)
{
case EVENT_COMMIT:
if (inTimer)
{
// Currently in the timer, so post a deferred callback and dim the button to acknowledge that it has been pushed:
PostDeferredCall (DeferredCommandButton, NULL);
// Dim the command button to acknowledge action
SetCtrlAttribute(panel, control, ATTR_DIMMED, 1);
}
else
{
// Do the button action here
...
// Then re-enable the button:
SetCtrlAttribute(panel, control, ATTR_DIMMED, 0);
}
break;
}
return 0;
}
Message Edited by msaxon on 08-22-2005 01:32 PM
08-22-2005 11:27 AM
Martin,
The problem with the PostDeferredCall suggestion is that because this application is processing system events while it's in the timer callback, it will still be possible for the deferred callback to be called before the timer callback exits. So, at a minimum, he would need to check the inTimer flag in the deferred callback as well, and then keep reposting the callback while the flag is enabled. Doing this, your solution should work, but it does not guarantee that the command button callbacks are performed in the same order that the user clicked on the buttons. I don't know whether or not this is a requirement. In case it is a requirement, I was discussing this problem with a co-worker, and we came up with a possible solution:
Whenever each callback is called, an 'action' token specific to that callback is placed on a queue. The timer callback would also post its own action on this queue. This way, all the callbacks, including the timer callback, can exit immediately.
Then, in order to remove items from the queue and perform the corresponding actions, there would be a separate timer that will check the queue and perform an action whenever the queue is non-empty. This timer should be an asynchronous timer (toolslib\toolbox\asynctmr.fp) so that its events are automatically sent to a different thread. This removes the need to call ProcessSystemEvents while performing the action, since the UI can't be blocked from a different thread.
Finally, the queue should be the thread-safe queue that is available in the utility library, so that the queue data is automatically protected from accesses from different threads.
Luis
08-22-2005 02:26 PM
Posting a deferred callback works well. As Luis pointed out, I also needed to repost the callback if the timer had not completed when the deferred event was processed. The timer active flag was already in place to prevent the timer itself from becoming re-entrant, so this was a fairly quick fix. Thanks for the help.
08-22-2005 02:28 PM
Posting a deferred callback works well. As Luis pointed out, I also needed to repost the callback if the timer had not completed when the deferred event was processed. The timer active flag was already in place to prevent the timer itself from becoming re-entrant, so this was a fairly quick fix. Thanks for the help.
While I went with the deferred callback approach to solve this problem, I have a similar situation in another program I am working on. I had considered trying to place a scheduler routine inside the timer callback used for the status updates and processing flags set by the user controls in the timer callback, but the idea of creating a separate command event queue and using an async timer to process them will be a much better way to go. Thanks for pointing me in the right direction.
I ask one question and get two very usable solutions.
Thanks to everyone.
08-24-2005 08:10 AM
The sample I posted showed that the callback was reposted if still in the timer!
Still, glad you got it sorted.
Martin.
08-25-2005 07:31 AM
@msaxon wrote:
The sample I posted showed that the callback was reposted if still in the timer!