LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

CmtGetLock behavior with OPT_TL_PROCESS_EVENTS_WHILE_WAITING

I normally use the windows SDK synchronization functions to protect data in multi threaded applications, but every now and then I use the CVI Lock functions and often run into problems for example....

 

I have an application that loads a DLL.  Both the main application and the DLL have multiple threads.  The DLL has a worker thread that gathers data, while holding a lock (call it dll_lock) and has a timer on the UI that updates the DLL's UI while also holding the lock.


The main application periodically reads some data from the DLL (from a UI Timer callback). This read function holds the dll_lock while copying the data.

 

The whole things runs for hours without problems until I call "RunPopupMenu" then my UI thread hangs.  The UI seems to hang even if the dll's worker thread and timer are not running in which case the RunUserInterface thread is the only one getting the lock.  The UI hangs on the first call to CmtGetLock(dll_lock) (in my dll when the Timer in my main applications calls the dll read function.) after calling RunPopupMenu.

 

The application freezes only if I have the OPT_TL_PROCESS_EVENTS_WHILE_WAITING flag set for the dll_lock.  If I set the flag to 0, RunPopupMenu, doesn't hang my application, but according to the documentation for CmtNewLock, I should set the flag because I do get the lock in panel callbacks and I update panels while holding it?

 

Why does RunPopupMenu freeze my UI in this case when nothing else I have tested does? 

 

So far as I can tell, the CmtGetLock function has some sort of event handler that calls a subset of the panel callbacks while blocked when the OPT_TL_PROCESS_EVENTS_WHILE_WAITING flag is set?  Does this also mean that with the flag set I can have event callbacks called from a thread that is not the RunUserInterface thread? (called from the blocked CmtGetLock function on some other thread?) 

 

Also, because event callbacks from the CmtGetLock are nested, calling CmtGetLock from multiple panel event callbacks (clicking a button a dozen time), while waiting on a lock, will happily over-flow the stack and GPF an application.

 

I don't think this behavior is intuitive and I think some further explanation on how the locks work is required in the documentation or a knowledge base. 

0 Kudos
Message 1 of 4
(3,438 Views)
When the flag is set, CmtGetLock processes UI events for the current thread while waiting for the lock. This is similar to calling ProcessSystemEvents in the body of CmtGetLock while waiting for the lock. For example, a possible implementaton of CmtGetLock would be:
 
int CmtGetLock(int lock)
{
    while (!GetLockWithoutEvents(lock))
        ProcessSystemEvents();
}
 
Note that only UI events for windows/panels belonging to the current thread will be dispatched here (by ProcessSystemEvents). So panels/windows created by other threads will not have their events processed. Of course, calling CmtGetLock can result in nested calls to callbacks. That is one reason it is not a good idea to use this flag other than in very special circumstances - like in some COM (ActiveX) threads.
 
I am not sure why your program deadlocks - you may want to post a simplified version of your code that can be used to reproduce this problem. It is possible the deadlock happens if either the menubar or the panel in the call to RunPopupMenu were created in a thread other than the current thread calling CmtGetLock. If this is the case, then make sure the menubar and panel used in RunPopupMenu are created in the same thread, and RunPopupMenu is always called from this thread.
 
I recommend that this flag should be used only in very special cases. In my opinion, doing UI operations from multiple threads is a bad idea on Windows because panels/windows are owned by the threads that create them, and accessing them from multiple threads is just asking for trouble. You can always use the PostDeferredCallToThread function to make sure UI tasks are only done in the appropriate UI thread - the one that created the panel/window. It is an even better idea to restrict all UI creation and operation to just one thread in the program - makes designing, writing, and debugging the program much simpler.
0 Kudos
Message 2 of 4
(3,419 Views)

I disagree that you shouldn't update UI controls from multiple threads. CVI Controls are thread safe and that is one of the stand out benefits of using CVI for data acquisition applications.  I can update my time sensitive , data intensive, controls from a data acquisition thread without having to queue event messages.

I've used PostDeferredCall in some cases, but it has fallbacks.  For example, when the user drags the panel around, resizes it, or just hold down the mouse button on the title bar, the dialog messages are blocked.  Using PostDeferredCall queues all of the callbacks and then bursts through them.  I also have to pass large amounts of data to be plotted which means I have to dynamically allocate and copy the data on a PostDeferredCall or use another very large ThreadSafeQueue.  This causes my memory footprint to balloon when those events are queue. 

Alternatively, I can plot the data directly to the control from my data acquisition thread. No additional memory allocation, no queued event callbacks.  So far as I know, this is perfectly acceptable in CVI, though I agree would hang a Win32 application in seconds.  CVI does not use Win32 controls.

0 Kudos
Message 3 of 4
(3,401 Views)
Yes, CVI controls are NOT win32 controls and are thread-safe. And yes, they can be updated from any thread and this is great. But CVI top-level panels are win32 windows and so one is not entirely free of win32 messaging/threading restrictions. While just updating controls from any threads is fine, running popups, tracking loops, and modal activities from inappropriate threads can deadlock - may be that is what is happening in your application.
0 Kudos
Message 4 of 4
(3,384 Views)