LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Multi-threading a TSQ Callback

I have been using multi-threaded applications for a long time.  This has amounted to spawning transitory threads to do a task and then go away, or spawning continuous tasks to update displays or process some data.  Separate threads were used so that the user interface is always responsive.  Has worked fine.
 
I now have an application to create which receives packets of data on the Com port, of varying size, and must process them.  These will arrive very fast, about every 10 milliseconds.  I think the best way to do this is have the Com Callback pass the data to a processing function via a TSQ, and have that processing performed in a different thread.
 
I'm not sure how to ensure that the processing function executes in a different thread.  Writing to the TSQ will issue an event to the TSQ callback, but how does one ensure that that TSQ callback is in a different thread.  This is not obvious in the documentation I've seen so far.
 
My only thought so far goes like this:
 
main()
{
   OpenComConfig, etc;
   CmtNewTSQ(...);
   InstallComCallback (COM1, LWRS_RECEIVE, 5, 0, ProcessInputStream, 0);
 
   CmtScheduleThreadPoolFunction (DEFAULT_THREAD_POOL_HANDLE, ProcessInputStreamThread, NULL, NULL);
 
   runuserinterface;
}
 
void CVICALLBACK ProcessInputStream (int portNo,int eventMask, void *callbackData)
{
Get the packet from the port
 
write the packet to the TSQ
}
 
 
static int CVICALLBACK ProcessInputStreamThread(void *ctrlID)
{
   CmtInstallTSQCallback(..ProcessDataTsqCB...);
  
   while (threadFlag)
      {
      ProcessSystemEvents;
      Delay(1.0);
      }
}
 
void CVICALLBACK ProcessDataTsqCB (...)
{
   Process the data packet;
}
 
Does this seem reasonable?  Does the "ProcessSystemEvents(); Delay(1.0);" keep this spawned thread alive so that the TSQCallback will respond to every event?
0 Kudos
Message 1 of 8
(3,737 Views)
Your code structure looks reasonable to me. The thread you register the TSQ callback in is the one it will execute in, so I think you are on the right track.
Martin Fredrickson
Test Engineer

Northrop Grumman
Advanced Systems and Products
San Diego, CA 92128
0 Kudos
Message 2 of 8
(3,732 Views)
Except I would prefer Sleep(1000) in place of Delay(1.0).  This is because the SDK function Sleep() suspends itself for the duration and allows other threads to run efficiently, while the CVI function Delay() is a busy one, taking 100% CPU while it runs. Depending on how large your data and/or queue size is, it might be better to reduce this time to 50 or 100 ms.
 
JR
0 Kudos
Message 3 of 8
(3,717 Views)
jr_2005, you bring up another question regarding Delay() vs Sleep(), and the time value:  Naturally I want things to work efficiently, to not expend cycles in a wait loop, but I want the TSQ callback to respond promptly to an event.  Events will occur as fast as every 10 milliseconds. 
 
Does this while loop just need to keep the Thread alive, or does it need to have the call to ProcessSystemEvents() somewhat synchronized to the rapidity of the TSQ event.  IE, does the time period in the Sleep() need to be 1 second, just to keep the thread alive, or 5 milliseconds to process system events rapidly?
0 Kudos
Message 4 of 8
(3,708 Views)

I'm not familiar with Thread Safe Queue Callbacks in particular, but if they work in a similar manner to other types of CVI callbacks I think their action is triggered by the ProcessSystemEvents() function. This probably means that you will need to call ProcessSystemEvents() at a faster rate than the callbacks can be generated, to ensure that you do not miss any of them and that a backlog does not build up in the queue. I tend to use a Timer control set to 0 seconds and use its callback to call ProcessSystemEvents(), to make sure it gets called whenever CVI has nothing better to do. Perhaps an alternative approach for you would be to put a ProcessSystemEvents() call at the very end of your ProcessDataTsqCB() function, so that if another packet was pending in the queue it could be handled immediately. You should code the function to be re-entrant, in this case.

Also bear in mind the granularity of the Windows timer used for the Sleep() function; in my experience this is usually 10 or 15 ms depending on the PC hardware configuration, meaning that if you ask for between 1 and 10 (or15) you will always get 10 (or 15). Caught me out once when I had Sleep(1) in a timeout loop - this turned out to take15 times longer than I expected. Smiley Tongue

JR

0 Kudos
Message 5 of 8
(3,701 Views)
Very good points.  I'll code up some experiments that display some response times.  I did that a couple days ago to understand how responsive creating a thread was, when there was a lot of stuff going on in other threads.  I'll add the TSQ to that code and examine all that.
0 Kudos
Message 6 of 8
(3,701 Views)

Here is an approach that avoids TSQ and ProcessSystemEvents() issues:

  • Call CmtNewThreadPool() to create a thread pool holding a single thread (assuming that you want to process the packets strictly in sequence).

  • In your Com Callback:

    • Allocate a block of memory sufficient to hold the current packet (or the largest possible packet), and copy the new data to it.

    • Call CmtScheduleThreadPoolFunction(), specifying the thread pool you created, and passing a pointer to the new memory block.

  • In the scheduled function:

    • Process the packet, free the memory and return, making the thread available for the next iteration.

Remember to call CmtDiscardThreadPool() when you're done.

 

Regards,

Colin.

 

0 Kudos
Message 7 of 8
(3,673 Views)

Another way to get your thread to release resources back to the OS but still maintain a pretty good response time to events is to use the MsgWaitForMultipleObjectsEx() found in the windows SDK.  This avoids some of the issues with Delay() and Sleep().

A generic example:

HANDLE hWndThread;

hWndThread=(HANDLE)GetCVIWindowHandleForCurrentThread();
while (whatever_makes_you_loop_stop != true)
  { 
    MsgWaitForMultipleObjectsEx(1, &hWndThread, 500, QS_ALLEVENTS|QS_ALLINPUT, MWMO_ALERTABLE);
    ProcessSystemEvents();
  }

You can play around with various settings of MsgWaitForMultipleObjectsEx() if you know which types of windows events you want to wake your thread up.  You do not need to call ProcessSystemEvents() if the thread does not do anything directly related to a CVI event.  This is also a very useful way to put a thread to sleep while another thread completes some action.

More on this very flexible function can be found at the MSDN website http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/msgwaitformultipleobje....

 

 

 

0 Kudos
Message 8 of 8
(3,632 Views)