Crash after killing a thread

I have an application where I perform calculations in a thread. Since some of the calculations can be lengthy I have added the ability to kill the thread from the user interface. When testing, the application will silently crash about one in four times when I kill the thread. I have included part of the crash log below. Funny thing is that I have a similar application and it does not seem to crash. Only real difference is the calculation being done in the thread. All code in the thread is pure Xojo (no plugins or dylib calls). What could be causing the crash??

Process:               APMInt.debug [477]
Path:                  /Users/USER/Desktop/*/APMInt.debug.app/Contents/MacOS/APMInt.debug
Identifier:            com.apmathsoft.apmint
Version:               ??? (1.2.0.3.0)
Code Type:             X86 (Native)
Parent Process:        ??? [1]
Responsible:           APMInt.debug [477]
User ID:               501

Date/Time:             2015-04-06 23:00:12.191 -0500
OS Version:            Mac OS X 10.10.2 (14C1514)
Report Version:        11
Anonymous UUID:        58B378C7-E6E9-BBAD-7981-B02B2271E0C8


Time Awake Since Boot: 53 seconds

Crashed Thread:        12

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x00000000b05aaa80

VM Regions Near 0xb05aaa80:
    Stack                  00000000b0527000-00000000b05a8000 [  516K] rw-/rwx SM=COW  
--> 
    Stack                  00000000bc000000-00000000bf800000 [ 56.0M] ---/rwx SM=NUL  


Thread 12 Crashed:
0   com.xojo.XojoFramework        	0x00480238 rnd + 4326
1   com.xojo.XojoFramework        	0x004f874c RuntimeRaiseException + 305
2   com.xojo.XojoFramework        	0x004a7ce5 RuntimeSwapPStringLocks + 2007
3   com.xojo.XojoFramework        	0x0050e9c7 StringFromCFString + 386
4   com.xojo.XojoFramework        	0x0045280c RuntimeBackgroundTask + 35
5   com.apmathsoft.apmint         	0x00296f4b apmint_module.apmint_fft_rdft_all%%i8i8p + 212493
6   com.apmathsoft.apmint         	0x002dbd75 apmint_module.apmint_fft_multiply%o<apmint_module.apmint>%o<apmint_module.apmint>o<apmint_module.apmint> + 8247
7   com.apmathsoft.apmint         	0x00308092 apmint_module.apmint.factorial%o<apmint_module.apmint>%o<apmint_module.apmint> + 6868
8   com.apmathsoft.apmint         	0x00237cc8 WindowCalc_Integer.WindowCalc_Integer.thread_action%%o<WindowCalc_Integer.WindowCalc_Integer>o<Thread> + 11749
9   com.apmathsoft.apmint         	0x0023f2a1 Delegate.IM_Invoke%%o<Thread> + 83
10  com.apmathsoft.apmint         	0x0023f242 AddHandler.Stub.0%% + 51
11  com.xojo.XojoFramework        	0x0050fc51 threadRun + 909
12  libsystem_pthread.dylib       	0x9956ee13 _pthread_body + 138
13  libsystem_pthread.dylib       	0x9956ed89 _pthread_start + 162
14  libsystem_pthread.dylib       	0x9956ce52 thread_start + 34

Thread 12 crashed with X86 Thread State (32-bit):
  eax: 0x00005088  ebx: 0x002d9cf2  ecx: 0x020c4810  edx: 0x00000411
  edi: 0xb05a59f8  esi: 0xb059aa6c  ebp: 0xb059a9b8  esp: 0xb059a990
   ss: 0x00000023  efl: 0x00010206  eip: 0x00480238   cs: 0x0000001b
   ds: 0x00000023   es: 0x00000023   fs: 0x00000023   gs: 0x0000000f
  cr2: 0xb05aaa80
  
Logical CPU:     0
Error Code:      0x00000006
Trap Number:     14

How do you kill the thread?

I kill the thread using the thread.kill method. Code used is below:

[code] If Thread_computing.State <> Thread.NotRunning Then
thread_was_killed = True
Thread_computing.Kill

While Thread_computing.State <> Thread.NotRunning
  app.SleepCurrentThread(50)
Wend

End If[/code]

Have you tried to add logging to see exactly where the crash happens?

In the beginning of Cocoa UserCancelled didn’t work. Somehow I never changed this. I’m using a boolean variable instead that is set when the user cancels and this is always checked before doing something. With this I don’t have to kill the thread outright.

No, It’s quite a large chunk of code. Note that it does not crash unless the thread it is running in is killed.

I have removed all pragmas and I capture all exceptions in the thread and when it crashes I am not getting an exception at all.

Can you post the code in the thread? Do you catch exceptions within it?

As the error reported is a SIGSEGV (segmentation violation), which means your code tries to access – while the thread is being killed – a by now invalid address in memory. One example is a timer, which updates the UI, thereby trying to access variables in the thread subclass.

I would follow Beatrix’s advice. Add a boolean flag to the thread and set it when the user cancels. In the thread, check the flag during every loop iteration and gracefully exit the thread if the flag has been set.

You can do the same thing with an exception handler in the Run event of the thread.

// All your code

Exception err as ThreadEndException
  // The thread is ending so
  // clean up the mess

  raise err // Pass it on

Please post the entire crash log somewhere.

I created a feedback case and attached a full crash log there.

(https://xojo.com/issue/38863)>]link text

The run method of the thread event is below. In this case I am computing a very large factorial. The method that is crashing is a FFT multiply that eventually gets called from the factorial method. I don’t want to post it as it is very large. It is noteworthy that when it does not crash I get a ThreadEndException. When it crashes I get no exception at all ( setting a breakpoint in the exception block).

If all else fails I could as follow Beatrix’s advises and create a boolean and add to a few of the math routines (in my case add, subtract, multiply and divide) and raise an exception when the boolean is true. The code is much more complex than a simple loop involving many math functions so there is no graceful way to exit other than an exception. I was hoping that killing the thread and using [i]ThreadEndException[/i would suffice.

As I stated earlier, I have another math app that I have submitted to the mac store that I am doing the same thing (running computations in a thread and letting the user cancel) and this app does not crash. In that app and this one the thread does not interact with the GUI, timers, network connections, etc. The mac app store app calls into a dynamic library to boost the speed of some of the algorithms. This app that is crashing does not even do that. It mostly creates memory blocks, performs algorithms on them and returns a result.

[code] // all calculations performed in this method

Select Case algorithm_to_perform

Case 1 ’ multiply
If register_depth > 0 Then
registers(0) = registers(0) * result
result = registers(0).to_text
registers_need_popping = True
End If

Case 2 'divide
If register_depth > 0 Then
registers(0) = registers(0) / result
result = registers(0).to_text
registers_need_popping = True
End If

Case 3 'add
If register_depth > 0 Then
registers(0) = registers(0) + result
result = registers(0).to_text
registers_need_popping = True
End If

Case 4 'subtract
If register_depth > 0 Then
registers(0) = registers(0) - result
result = registers(0).to_text
registers_need_popping = True
End If

Case 5 ’ prime number?
secondary_result_boolean = result.is_likely_prime

Case 6 'least common multiple
If register_depth > 0 Then
registers(0) = apmint_lcm(registers(0), result)
result = registers(0).to_text
registers_need_popping = True
End If

Case 7 'greatest common divisor
If register_depth > 0 Then
registers(0) = apmint_gcd(registers(0), result)
result = registers(0).to_text
registers_need_popping = True
End If

Case 8 'y raised to x power
If register_depth > 0 Then
registers(0) = registers(0).Pow(result)
result = registers(0).to_text
registers_need_popping = True
End If

Case 9 'factorial
result = result.factorial

Case 10 ’ number is a perfect square?
secondary_result_boolean = result.is_square(secondary_result_apmint)

Case 11 ’ number is a power of another integer?
secondary_result_boolean = result.is_power(secondary_result_pair)

Case 12 ’ number of bits
secondary_result_uint64 = result.bits

Case 13 ’ square root
result = result.Sqrt

Case 14 ’ remainder
secondary_result_pair = apmint_divide_remainder(registers(0), result)
result = secondary_result_pair.Right

End Select

Exception err
an_exception_occurred = True
If err IsA ThreadEndException Then
Raise err
Else
handle_compute_exceptions(err.message)
End If[/code]

After some testing and pondering I believe I know the aggravating causes of the crashes that I am experiencing. I have noted the following:

The crashes are much more frequent in the debugger versus built application.
A prior app that I made that does the same thing does not crash (it uses some C code to speed up methods in the thread).
The app that crashes always crashes while in a lengthy, compute intensive method.

From the above, I believe that having a method in a thread that has a long running time, compute intensive, and in this case makes no calls to the framework will cause issues when killing the thread.

To test this assertion, I broke up the offending method into about 8 pieces and retested and have not been able to crash the app by killing the thread. This makes me believe that there is a race condition, timeout, or other time sensitive issue related to killing a thread where a long running method will cause crashing issues. I believe that the built application crashes less frequently upon killing the thread simply because the built application runs the method faster. My previous app does not crash because the offending method is already broken up and makes C library calls that are much faster than native Xojo.

Maybe after Joe examines the applications that I submitted (before and after modification) he can enlighten us on more specifics of the internals and maybe make a change that will prevent such crashes.

The issue is that the exception handling system never expected there to be functions requiring more than 32kb of stack space for local and temporary variables. The bug will be addressed in a future release, but the workaround of splitting the function up is safe (and probably a good idea regardless).

Joe, thanks for the feedback. If this is inherent in the exception handling system would it apply to any exception and not just killing a thread. After looking through crashes reported in the forum there were several that looked similar with:

Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x00000000???????

with different addresses being written too at the time of the crash.

Examples:

link text
link text
link text

[quote=179406:@William James]Joe, thanks for the feedback. If this is inherent in the exception handling system would it apply to any exception and not just killing a thread. After looking through crashes reported in the forum there were several that looked similar with:

Exception Type: EXC_BAD_ACCESS (SIGSEGV) Exception Codes: KERN_INVALID_ADDRESS at 0x00000000???????[/quote]

These aren’t Xojo exceptions – these are Mach exceptions, triggered by the kernel. They’re basically OS X’s versions of signals (except that it has signals too, which are generated from the Mach exceptions).