CallLater / App.Quit not always working

I’m working with 2019r3.1 and a compiled web service (no UI) on Linux (64-bit). Here’s the code:

If shutdownRequest Then MessageAdd(CurrentMethodName + ": shutting down in 20 seconds.") // Breathing room for script completion & possible upload new exe Xojo.Core.Timer.CallLater(20000, AddressOf QuitApp) End If

The above is triggered in a timer (main thread) and works fine. Here’s the QuitApp method:

MessageAdd(CurrentMethodName) App.Quit()

This concept works most of the time, but not when the shutdownRequest is set after what appears to be a hanging thread because of an open HTTP socket.

What happens in that case is the “shutting down in 20 seconds” is being logged infinitely (timer works fine) but the QuitApp method is never logged / called.

Could it be that the hanging thread blocks CallLater somehow? Can I check that or is there a workaround?

Make sure your thread yields once in a while or those timers won’t fire. See Thread.Sleep.

The main App.Open only starts a bunch of timers like this one and finishes, so I don’t think I can yield anything. Right?

Also, the timer itself runs fine and, ehhh, on time and every time (logging each 5 second interval). So how could the CallLater not find time to fire?

(How many times can you use ‘time’ in a post…)

In what context do the timers run when the main app has quit?

I’m sorry, but I don’t really know how to answer that question. The App didn’t quit, App.Open finishes after having done a bunch of stuff, including:

MessageAdd("Starting shutdown timer.") timerShutdown = New Timer AddHandler timerShutdown.Action, AddressOf timerShutdownAction timerShutdown.Period = 5000 // Every 5 seconds timerShutdown.Mode = Timer.ModeMultiple

The code for the timer is in the first post. When the flag {App.shutdownRequest} has been set, it calls the App.Method.

This always works, except in the scenario I described earlier.

In both cases {timerShutdown} happily keeps on firing every 5 seconds, but the {QuitApp} is not called if there’s a hanging (not properly cleaned up - destructor didn’t run) thread / HTTPSocket (HTTP error?) in another part of the App. Therefore I was wondering if this somehow can mess up CallLater.

As a workaround I’ve added an extra counter in the timer that wil directly call the method after a couple of cycles. It’s not easy to test though as this scenario almost never happens…

You should probably stop accepting connections before going into your close down routine.

All connections are closed at that point in time. Except for the one that has not destructed correctly.

Still, why should it block the CallLater mechanism?

[quote=484300:@Arthur van den Boogaart]All connections are closed at that point in time. Except for the one that has not destructed correctly.

Still, why should it block the CallLater mechanism?[/quote]

I don’t see anything in your code setting this property to False:

Correct. Should that help or even matter? Does that influence CallLater?

My app is a web service which does not get incoming connections other then pages received from outgoing REST calls.

Also, this code works 99% of the time. It’s only a problem after the scenario I described earlier.

It’s not that the main thread(s) are busy since everything has been shut down too. The app has plenty of time, which is also witnessed by the precision of the timer going off. Even with every thread running full blast this mechanism works fine as I have sleeps all over the place…

[quote=484303:@James Dooley]I don’t see anything in your code setting this property to False:[/quote]

I’m wondering how this works though…

So does it stop accepting new ones and kill all outstanding REST (HTTPSocket) calls? So all threads waiting for a HTTP reply are going to be disrupted?

If that’s the case I won’t use it until after these threads have been gracefully exited…

If by web service you mean a console app / service application maybe you could use a different approach.

Your main thread will have some kind of loop which keeps the process running.

Rather than your shutdown using CallLater to trigger a method to execute app.Quit, why don’t you use a boolean property which indicates if the loop should continue. Your shutdown detection code would flip the property value which would exit the loop and end the process.

I appreciate the input, but how would this help me? Right now the app works fine and it’s heavily multi-threaded so I don’t see how or why I would have to change this. Not sure it would be a better design to begin with, but I’m open for good arguments!

My approach would mean that you don’t need to use CallLater which according to your diagnoses. doesn’t appear to be working correctly.

True. But I thought (a loop with only) a sleep for the main thread (in App.Open) was considered to be ‘not done’…

Oh, it looks like you are doing this using a Desktop project.
Is there any reason why you aren’t using a Console project since your app doesn’t have a GUI?

No sorry, it is a console web app…

Sorry, never touched a web app before.

Can you replicate this in a small project that you can upload here for us to look over? If you need to block a connection fir the test just try to connect to a random port on localhost.

Yes it cleanly closed down everything and in my experience it is necessary for a correct clean up.

But then you need to track the threads and make sure they are correctly closed down, that than giving it a random 20 seconds and hoping for the best.

Thanks for the replies guys! Somehow I didn’t get an email from the forum.

@ - A small project… Dunno about that, will take some time as the App is fairly complex with lots of timers starting threads which spin up threads running (e.g.) XojoScripts in other threads. I’m pretty sure that’s related to the issue.

@James Dooley - I already track the threads because I need to know if they’re finished. That gets double checked when the CallLater fires after 20 seconds. If it fires.

For now I’ll settle for extra counter I’ve put in. Also I’m going in to see if I can find a way to catch and destruct the hanging thread (reference?) somehow.

Just for the record: CallLater is not completely clean though. Maybe it gets queued (?) after (blocked by) the hanging thread or something? No clue how this stuff works internally.