LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

shutting down a panel in one thread from another thread.

For various reasons, I have a program with two threads (actually more than two, but only two are concerned here). One is the main panel that runs at startup, another is a Virtual O'Scope panel with a real-time display. Everything works more or less well, until it's time to exit the program.

The main program starts by calling a routine to display the VO'scope; this routine calls CmtScheduleThreadPoolFunctionAdv to schedule the VOscope thread with the function VOscopePanelMain.

 

VOscopePanelMain initializes things, displays the VOscope panel, and then calls RunUserInterface(); to process events in the VOscope panel.

 

When it comes time to close the window, closing the panel (the X box in the upper right corner) triggers the panel callback CB_VoscopePanel, which processes the EVENT_CLOSE: event by calling QuitUserInterface(0); which, in turn, causes RunUserInterface to return to VOscopePanelMain, which then shuts things down, closes the panel properly, and exits. So far so good.

 

int CVICALLBACK CB_VoscopePanel (int panel, int event, void *callbackData,
        int eventData1, int eventData2)
{
    int    iPanelHeight, iPanelWidth, iV2ControlLeft, iV2ControlWidth, iWidth,
        iT2ControlTop, iT2ControlHeight, iHeight, iLeft, iGap, iScreenTop, iScreenLeft,
        iTop, iBoxWidth;
    
    switch (event) {
        break;
    case EVENT_GOT_FOCUS: //happens when first displayed or refreshed
    case EVENT_PANEL_SIZE: //size the controls on the panel
       ... do stuff here;
        break;
    case EVENT_CLOSE:
        QuitUserInterface(0);  //stop VOscopePanelMain, which in turn closes the panel and cleans stuff up.
        break;
    }
    return 0;
}

 

However, I also want the panel to stop when I close the main program. The only way that I know how to do this cleanly is to have the main program (which has closed all of its panels and is in the process of shutting down) call VOSCOPE_Close_VOScope () which, in turn, calls CallPanelCallback (iHandle_VOscope, EVENT_CLOSE, 0, 0, 0); (which forces a call to CB_VoscopePanel above with the EVENT_CLOSE event), which should call QuitUserInterface, which should cause the RunUserInterface in VOscopePanelMain to return and let it continue to shut down. In addition, after calling CallPanelCallback, the shutdown routine calls CmtWaitForThreadPoolFunctionCompletion to wait for the VOscopePanelMain thread to actually quit and clean up before proceeding.

 

But, of course, it doesn't since, and it took me a while to realize this. The call to QuitUserInterface isn't coming from inside of the VOscopePanelMain thread, it's coming from the main panel's thread - which is already in the process of shutting down. So, the main panel thread is telling itself to quit, VOscopePanelMain never gets the QuitUserInterface message, and things stall.

 

So: how do I have one thread tell a panel in another thread to cleanly close? Or do I have to get complicated and either replace RunUserInterface in VOscopePanelMain with a loop that processes events manually and looks for a flag, or figure out something with a thread-safe queue? Any help appreciated.

0 Kudos
Message 1 of 5
(4,796 Views)

This issue could be addressed by a call to PostDeferedCallToThread () issued from the main thread to the scope thread.The deferred callback should only call QuitUserInterface and the main thread should stay in a loop waiting for the scope thread to be finished before proceeding to shut down the program (see CmtWaitForThreadPoolFunctionCompletion).



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 2 of 5
(4,791 Views)

Still having a problem. The deferred callback never gets called.

This is probably because the VOScopeMain thread is in a different thread pool: VOscopePanelMain, created when the main program starts up.

		CmtStatus = CmtScheduleThreadPoolFunctionAdv (Level1_poolHandle,
													  VOscopePanelMain, NULL,
													  THREAD_PRIORITY_HIGHEST,
													  CB_NotifyThreadFunctionExecState,
													  (EVENT_TP_THREAD_FUNCTION_BEGIN | EVENT_TP_THREAD_FUNCTION_END),
													  0,
													  CmtGetCurrentThreadID(),
													  &VOSCOPE_ThreadFunctionID);

0 Kudos
Message 3 of 5
(4,742 Views)

Sorry for delay in answering, it took me a while to find time to build up a working example.

 

The attached program spawns a thread in a new thread pool and permit you to choose whether to close it from the main thread or the spawned thread itself.

It appears that in such a minimal configuration the process works as expected. There may be some different configuration in your actual program that prevents this.



Proud to use LW/CVI from 3.1 on.

My contributions to the Developer Community
________________________________________
If I have helped you, why not giving me a kudos?
0 Kudos
Message 4 of 5
(4,596 Views)

you sample pjt is not working in my cvi 2005 tool(i think this project built in latest version)

0 Kudos
Message 5 of 5
(3,011 Views)