Looking for some clarity on Thread.kill()

I’m looking for more information about exactly what happens when I call Thread.kill(). The docs are pretty sparse: http://documentation.xojo.com/index.php/Thread.Kill - essentially it says the stack will unwind, destructors will fire, etc… but I’ve run into a problem where when I have a long-running thread (can take several minutes to execute) is killed, the entire app is being killed by the OS. No exceptions are thrown - the app is just unceremoniously killed by the OS.

My thread is a complex one: it makes a bunch of network calls, constructs and manipulates a bunch of objects, calls a bunch of methods that go several levels deep, etc.

So: Do I need to do anything to make my thread “easier” to kill? Should I be periodically putting it to sleep to force a context switch so kills are cleaner? Do I need to change it’s priority? (it’s already set to priority 4 while the main thread is at 5).

Rather than calling .kill on the thread do I need to set a variable in the thread like timeToDie = true and then periodically have the thread check the value of timeToDie, and force it to return out of everything it was doing naturally?

Further: I’ve checked the console in OS X to see output from my app while it is running… the thread is quite noisy (as is the main thread as well) and when the app dies (shortly after calling .kill() on the long running thread) there are no entries in the console about the app at all… it just stops producing output and the generic message from Apple about how the app stopped working appears.

Any insight would be appreciated.

Hi Kimball,

The short answer is that allowing the thread to end by exiting a While-Wend (or other loop) is the cleanest way to end a thread. Using the kill command can cause unintended issues, such as memory writing immediately stopped, data improperly written before killing the thread, and many other possible issues.

An example of how to make a thread be killed cleaner is shown in Example 2-2 in the free book at Threads and Timers.

In Example 2-3 it shows that killing a thread will the Kill command can cause weird errors, such as locking the GUI and other issues.

Yes, I would suggest this type of coding. In Example 2-2 I use the following While-Wend loop:

While (IsFinished = False) MyDouble = Microseconds Wend

Once the IsFinished flag is true, then the loop exits and the thread gracefully quits. All data can be properly written to prevent issues from locking up the GUI, providing valid numbers that were in the loop, and other ways to allow the variables and functions to exit properly.

If one of the functions take a larger amount of time, then the computer allows the function to properly execute and then exit.

This is a great question! :slight_smile:

Edit: If the microseconds value has only partially written to the MyDouble value and the thread was killed, then weird information may be in the MyDouble value, which can cause errors.

Good question. Thread.Kill is implemented internally by throwing the ThreadEndException. This exception will then bubble up until the thread is dead.

This raises some important issues:

  • If you are accidentally catching this exception, your thread is unkillable. This code pattern is bad:
Thread1.run
...
Catch e as Exception
  • A thread can only be killed while it is executing Xojo code - any blocking call (such as shell.execute, array.sort(), whatever) can not be killed until it returns.

  • I’m pretty sure that a thread can be killed when it is not at a Yield point (implicit or explicit) but I’m not 100% sure about that.

.[quote=379576:@Michael Diehr]I’m pretty sure that a thread can be killed when it is not at a Yield point (implicit or explicit) but I’m not 100% sure about that.[/quote]
Obviously, it cannot. The thread must be yielded in order for the Kill command to execute in the first place. Unless you’re killing the thread from within the thread, which is kind of silly

It doesn’t need to yield, just check for exceptions after a line finished.
So the ThreadEndException is handled.

You want to make sure all threads are stopped before quitting the app or closing the window it is instantiated on.

There was a bug at one point where closing a window left the thread running in the air, and that caused the app to crash.

[quote=379581:@Tim Hare].
Unless you’re killing the thread from within the thread, which is kind of silly[/quote]

Why is it silly? I’m no thread expert, but I use a fairly simple one to keep the UI alive while the thread controls some external hardware. There are a number of things that can go wrong during the process that the thread controls, and if one of those happens, I set an error code in a window property and then execute me.kill in the thread. This raises the Killed event, which calls main thread code that handles the error. I think without me.kill as an escape route, my thread.run event would be a pretty impenetrable mass of nested conditionals.

If it’s a 64 bit app then you might be running afoul of a problem with exception handling. I’ve seen crashes and strange message boxes related to threads that raise exceptions.

You might be able to get around is by adding a top level handler for the exception in the thread and then just returning from it and not letting the exception bubble up out of the thread. something like:

exception exc as ThreadEndException
return

at the bottom of the thread.run method. Before you do that though you should try to see if it works OK when compiled as a 32 bit app. In my testing I was able to find several exceptions that could happen in threads and cause my app to crash when compiled to 64 bit but that were properly caught and logged so that I could debug them if I ran it as a 32 bit app. There are still some fish in 64bit exception handling in threads and I suppose that might extend to even handling it’s own threadEndException. My feedback report about exception handling in 64 bit apps is here:

<https://xojo.com/issue/50953>

if you find that you can get a regular exception popup or that nothing happens when compiled to 32 bit, or that catching the exception before it causes your thread to actually stop and doing a clean return from the run method stops the problem then you might be having the same problem.

The LR explicitly states that is discouraged: [quote]A thread should not kill itself. Instead, just return from the Run event handler to allow the thread to terminate.[/quote] @Julia Truchsess : my workaround is that I override the Kill method in my Thread subclass and check for App.CurrentThread = me if that is the case, I raise a special Exception which in turn is caught and silently ignored in an Exception block at the end of the Run Event handler. If the Kill is coming from another Thread, I check if the Thread about to Kill is Suspended in which case I Resume it before calling Super.Kill. That way the actual working code can freely call to Kill on any Thread, own or foreign in any state.

@James Sentman : I’m feeling your pain. Thread Kill in 64 Bit is quite crash prone, esp. if it is interacting with external plugins. Unfortunately your workaround to catch the ThreadEndException and return didn’t help in my case. But there is also <https://xojo.com/issue/48021> which seems to be fixed, so maybe 2018r1 will be the cure.

@Eugene Dakin : The alternative solution to Kill by setting a flag which ends the run loop may work well if the Thread is of a kind that has something like a run loop. If the Run event just calls some code paths outside that may not be aware they were called from a Thread at all, such a solution is quite messy to implement. Further, the Kill method seems to be synchronous (blocks the caller till the Thread has stopped) whereas setting a flag is not. I had to implement quite an ugly workaround to wait for the Thread to finish itself on some short-cut paths as a workaround for the current 64-bit trouble. (The thread in question is processing input which may change before the processing is done, in which case it is killed and restarted)

Ha, I did not know that, thanks! Return seems to work just fine, and is obvious in hindsight. Now I see why Tim said me.kill is silly :slight_smile:

Thank you everyone for all the input and extra information. Based on the notes people shared about issues with exceptions with 64 bit builds (I’m still shipping 32 bit, but not for long) I opted for the approach of setting a flag in the thread when it is time for it to stop. Periodically I check this flag during the course of events that the thread executes, and if it’s been set to indicate it’s time to quit the thread, I gracefully just return from everything all the way until returning from run() of the thread. This has completely resolved the crashing problem reported by one of my users, and also solved another long-standing problem that I had no idea was related to the fact this thread was being killed rather than gracefully returning.

Thanks again!