LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

wait for timer callback to complete

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?

0 Kudos
Message 1 of 9
(5,034 Views)
Couldn't you just dim the command buttons at the start of the timer callback, and then undim then when you are ready to leave the timer callback?
 
Luis
0 Kudos
Message 2 of 9
(5,030 Views)
That would be a good idea for some situations, but in this case I would have two problems.  The normal polling cycle for the timer callback is about 100mS.  That is probably a bit fast to dim/undim all the user command buttons (there are a lot) each timer event, but would probably work ok.  The more significant problem is that it would lock out the user from those controls during some of the longer timer callback processing cycles.  There are periodic intervals where the unit response is slow (1-5 seconds).  We don't want to lock out the operator from issuing the commanded actions during those periods.  The trick is how to keep the command button queued up until the timer callback is completed.     

Message Edited by mvr on 08-21-2005 01:32 PM

0 Kudos
Message 3 of 9
(5,030 Views)

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

--
Martin
Certified CVI Developer
0 Kudos
Message 4 of 9
(5,018 Views)

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

Message 5 of 9
(4,997 Views)

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.

0 Kudos
Message 6 of 9
(4,988 Views)

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.

0 Kudos
Message 7 of 9
(4,986 Views)

The sample I posted showed that the callback was reposted if still in the timer!

Still, glad you got it sorted.

Martin.

--
Martin
Certified CVI Developer
0 Kudos
Message 8 of 9
(4,967 Views)


@msaxon wrote:

The sample I posted showed that the callback was reposted if still in the timer!



I see that now that I look back at it.  Works great, thanks again for the help.
0 Kudos
Message 9 of 9
(4,940 Views)