From Friday, April 19th (11:00 PM CDT) through Saturday, April 20th (2:00 PM CDT), 2024, ni.com will undergo system upgrades that may result in temporary service interruption.

We appreciate your patience as we improve our online experience.

LabWindows/CVI

cancel
Showing results for 
Search instead for 
Did you mean: 

Quit program outside of main panel callback?

Solved!
Go to solution

Hi!

 

I want to terminate my program early as part of some error handling. Normally, the program terminates by calling QuitUserInterface() followed by some shutdown stuff like closing communications, cleaning up objects and such. I have tried both running QuitUserInterface() and explicitly calling my window callback with EVENT_CLOSE (normal call, not through callctrlcallback) and I can see in debug mode that it gets to QuitUserInterface() but it never exits the RunUserInterface() loop.

How am I supposed to terminate the program outside of the "normal" callback?

 

I have tried exit(1) as well, with success, but it doesn't feel quite right... It feels like there is probably some consequence to not doing proper clean up that I haven't noticed yet.

0 Kudos
Message 1 of 9
(1,418 Views)

In terms of shutting down your app with exit(1) there could be some consequences but only if you also access external resources such as some DAQ cards or similar.

As far as Windows resources are concerned (memory allocations, file, process, thread, mutex, and whatever other handles) the Windows process shutdown code is very efficient and thorough in finding and closing them all properly anyways. Where things could go wrong are possible 3rd party drivers (which might not perform a proper cleanup of kernel device driver resources they opened on behalf of your app) or real external hardware that you access, which might be left hanging in limbo, once your process ceases to exist.

 

Please note that NI hardware drivers do actually take part in the normal Windows process shutdown, so that should be of much less concern to you. It still could mean that external hardware connected to them is not properly reset or shutdown if you don't explicitly do it yourself.

Rolf Kalbermatter
My Blog
0 Kudos
Message 2 of 9
(1,396 Views)

Hi, and thanks for your input.

I have some communication going, and it seems important that CNVFinish and CNSFinish (for example) are run at the end of the program (the documentation is a bit vague as to why though...), which they aren't when using exit(). That's probably the most important part in this project. However, I do have some external resources like DAQ cards in other projects (that are hooked up to some equipment that are dangerous to keep running if the user interface crashes...) so I know I'll have to tackle this problem there as well.

 

I am mostly confused as to why QuitUserInterface() won't let me exit the loop. I have an inkling that is has something to do with surrounding callbacks/timers blocking the execution somehow. Any clue what could make it behave like that?

0 Kudos
Message 3 of 9
(1,390 Views)

The situation you are describing seems puzzling but it's not unusual: I have found myself in the same condition but in very specific and repetitive cases, i.e. in multithreaded applications where an incorrect error handling or exit signal tries to terminate the program without exiting all spawned threads. Another possible cause is calling QuitUserInterface from a thread other than the one running the main ()

 

In these cases if you are in the IDE you could try to break the execution: hopefully the program will stop inside the function that prevents it to terminate and this will help you in understanding how to properly handle the situation.



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 9
(1,387 Views)

Good idea about the threads. I try to avoid threading exacly because I know they can cause a lot of problems, but I have a single asynchronous timer which probably runs in its own thread. I tried destroying it before calling QuitUserInterface(), but it had literally no effect (aside from causing another thread to exist it should have little effect as it is timed at 2 seconds, so no pile-up of calls at least). I don't think I have any more threads than that. All I use are callbacks, which if I understand correctly are more like interrupts on a single thread, and normal functions. Besides, when QuitUserInterface is called normally it is from a callback so should not be a huge difference.

 

The documentation says "Call QuitUserInterface only from within a callback function invoked during execution of RunUserInterface.". I technically call it from a normal function, but that has been called from a callback. That could possibly be an issue, but I also tried calling it from inside the some random callback, with no effect there either!

 

When I break I usually end up at RunUserInterface (once I ended up in a callback due to communication being received), which really doesn't tell me anything...

0 Kudos
Message 5 of 9
(1,381 Views)

To discriminate what's going on I would start to cut out part of the functions and see if the program terminates correctly.

For example try not to start the asynchronous timer, or reduce the number of operations you are doing, reactivating them one by one until you find the one that breaks QuitUserInterface () normal behavior.

 

I know it's a stupid question but I saw some problems in the past here in the forum about that: are you calling RunUserInterface more than once in your program?



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 6 of 9
(1,376 Views)

Hi! I spent the day trying to pinpoint the problem - with some success after much trial and error. I took my code and minimized it as far as I could without losing the behaviour (see attachment). Basically, you can't reliably call QuitUserInterface from anywhere inside a callback triggered by an asynchronus timer - or any functions called through the callback. My guess is that these calls are run in a different thread and that causes some undefined behaviour.

 

To add to the trouble, QuitUserInterface can sometimes quit even from inside the async callback - but it's a total gamble. For a while I could reproduce a successful quit at several sections of my program - but as I changed the structure of my code it suddenly would not quit at these spots any more.

 

Mystery solved? Is this expected behaviour?

0 Kudos
Message 7 of 9
(1,310 Views)
Solution
Accepted by guybrush_threepwood

What you are seeing is expected if you consider that the asynchronous timer runs in a separate thread, so QuitUserInterface is likely to fail. This feature is clearly explained in the help for the functions of the instrument, and must be taken into account not only for the problem you are seeing but also and especially because you must carefully design the access to shared resources, variables and memory objects between threads.

 

To address your problem you could call a function with PostDeferredCall: with this the function is executed in the main thread and can stop the program gracefully.



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 8 of 9
(1,304 Views)

Thank you, I did not know about PostDeferredCall. It did the trick.

 

While I agree that it is clearly stated in the documentation for the async timer that it runs in a different thread, my problem was more that I did not understand how this affected RunUserInterface and QuitUserInterface. Maybe that's just my poor English or poor understanding of how RunUserInterface works, but I did not automatically understand that "Call QuitUserInterface only from within a callback function invoked during execution of RunUserInterface." meant "call only from main thread". Technically, the timer callbacks are invoked while RunUserInterface is running, though not from the same thread. But, but. Lesson learned.

0 Kudos
Message 9 of 9
(1,286 Views)