Preemptive Thread String Crash

In another thread, we were discussing that using Strings in preemptive threads should be safe. I found a way to crash it.

In this, there is a loop on the main thread, and a preemptive thread. Both of them assign a different string to a string (which is a Window Property) and then check to see if the string is what they assigned.

Since this is using a Preemptive thread, we would expect that sometimes the string would be different than what was assigned (since the other thread may have overwritten it).

However, we would not expect an app crash.

Crash log
Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   XojoFramework                 	       0x105efa1e9 UTF8DecodeAndAdvance(unsigned char**) + 7
1   XojoFramework                 	       0x105d61a4c 0x105c42000 + 1178188
2   XojoFramework                 	       0x105d6519f StringOpsClassic::CountFieldsText(StringStorageBase*, StringStorageBase*) + 337
3   XojoFramework                 	       0x105d6a56b StringCountFields + 66
4   preemptivestring.debug        	       0x10450ed93 String.$CountFields%i8%ss + 19
5   preemptivestring.debug        	       0x1049d3bc3 Window1.Window1.Button1_Pressed%%o<Window1.Window1>o<DesktopButton> + 2035 (/Window1:84)
6   preemptivestring.debug        	       0x1049d9014 Delegate.IM_Invoke%%o<DesktopButton> + 52
7   preemptivestring.debug        	       0x1049d9064 AddHandler.Stub.20%% + 52
8   AppKit                        	    0x7ff8123867ed -[NSApplication(NSResponder) sendAction:to:from:] + 337
9   AppKit                        	    0x7ff812386663 -[NSControl sendAction:to:] + 86
10  AppKit                        	    0x7ff812386595 __26-[NSCell _sendActionFrom:]_block_invoke + 131
11  AppKit                        	    0x7ff81238649e -[NSCell _sendActionFrom:] + 171
12  AppKit                        	    0x7ff8123863e6 -[NSButtonCell _sendActionFrom:] + 96
13  AppKit                        	    0x7ff8123832d2 NSControlTrackMouse + 1823
14  AppKit                        	    0x7ff812382b8f -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 125
15  AppKit                        	    0x7ff812382a56 -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 666
16  AppKit                        	    0x7ff812381e4b -[NSControl mouseDown:] + 666
17  XojoFramework                 	       0x105d26769 0x105c42000 + 935785
18  AppKit                        	    0x7ff8123807f3 -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 4582
19  AppKit                        	    0x7ff8122f9857 -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 313
20  AppKit                        	    0x7ff8122f9503 -[NSWindow(NSEventRouting) sendEvent:] + 345
21  XojoFramework                 	       0x105d31fad 0x105c42000 + 982957
22  AppKit                        	    0x7ff812aac1a0 -[NSApplication(NSEventRouting) sendEvent:] + 1456
23  XojoFramework                 	       0x105d22634 0x105c42000 + 919092
24  preemptivestring.debug        	       0x1048fd398 DesktopApplication._CallFunctionWithExceptionHandling%%o<DesktopApplication>p + 184
25  XojoFramework                 	       0x105ea8a59 CallFunctionWithExceptionHandling(void (*)()) + 254
26  XojoFramework                 	       0x105d225b2 0x105c42000 + 918962
27  AppKit                        	    0x7ff812666d2e -[NSApplication _handleEvent:] + 65
28  AppKit                        	    0x7ff81218a5aa -[NSApplication run] + 640
29  XojoFramework                 	       0x105ea727a RuntimeRun + 41
30  preemptivestring.debug        	       0x104939763 REALbasic._RuntimeRun + 19
31  preemptivestring.debug        	       0x1049df1aa _Main + 826 (/#main:109)
32  preemptivestring.debug        	       0x1049de623 main + 19
33  dyld                          	    0x7ff80e6c2345 start + 1909

I’m working on a demo project now.

3 Likes

Demo project:

preemptivestring1.zip (5.7 KB)

Running this crashes within about 10 seconds in the IDE, and a built app crashes within a minute. Xojo 2024R3, macOS 14.7 (Intel).

You reported this, I hope?

Working on it. I would appreciate if anyone else can reproduce (perhaps using Xojo for Windows? I only use macOS).

Here’s a variation: instead of using a Window.Property, the string is now a Global module property. In this version, debug runs in the IDE don’t crash, but a built app does crash the same way:

preemptivestring2.zip (5.9 KB)

So far I cannot reproduce. May be Intel-specific.

I’m calling it: I can’t reproduce this on Mac ARM (which explains why I never saw it in my testing).

Well for me example 1 crashes on a Macbook Air with M2 and macOS 15.0 when launched from IDE…built can’t test as I don’t have a license installed on it…

1 Like

Curious. But I’m sure @William_Yu will be able to get to the bottom of this.

With version 1 I’m also seeing crashes in a built app on M1 apple silicon. (I don’t have the IDE installed on macOS to test yet):

M1 Crash Log
-------------------------------------
Translated Report (Full Report Below)
-------------------------------------

Process:               preemptivestring [951]
Path:                  /Users/USER/Downloads/preemptivestring.app/Contents/MacOS/preemptivestring
Identifier:            com.xochi.preemptivestring
Version:                (1.0.0.0.0)
Code Type:             ARM-64 (Native)
Parent Process:        launchd [1]
User ID:               501

Date/Time:             2024-10-04 08:45:18.8894 -0700
OS Version:            macOS 15.1 (24B5055e)
Report Version:        12
Anonymous UUID:        FBBCB89E-FDE9-DC5F-A772-C2A232F4CD15


Time Awake Since Boot: 680 seconds

System Integrity Protection: enabled

Crashed Thread:        0  Dispatch queue: com.apple.main-thread

Exception Type:        EXC_BAD_ACCESS (SIGSEGV)
Exception Codes:       KERN_INVALID_ADDRESS at 0x0000000000000001
Exception Codes:       0x0000000000000001, 0x0000000000000001

Termination Reason:    Namespace SIGNAL, Code 11 Segmentation fault: 11
Terminating Process:   exc handler [951]

VM Region Info: 0x1 is not in any region.  Bytes before following region: 4375773183
      REGION TYPE                    START - END         [ VSIZE] PRT/MAX SHRMOD  REGION DETAIL
      UNUSED SPACE AT START
--->  
      __TEXT                      104d10000-1050fc000    [ 4016K] r-x/r-x SM=COW  /Users/USER/Downloads/preemptivestring.app/Contents/MacOS/preemptivestring

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   XojoFramework                 	       0x10604a9a8 UTF8DecodeAndAdvance(unsigned char**) + 4
1   XojoFramework                 	       0x105eb4108 0x105d8c000 + 1212680
2   XojoFramework                 	       0x105eb79dc StringOpsClassic::CountFieldsText(StringStorageBase*, StringStorageBase*) + 368
3   XojoFramework                 	       0x105ebd454 StringCountFields + 72
4   preemptivestring              	       0x104d153c0 String.$CountFields%i8%ss + 28
5   preemptivestring              	       0x1050c42a4 Window1.Window1.Button1_Pressed%%o<Window1.Window1>o<DesktopButton> + 992
6   preemptivestring              	       0x1050c7a0c Delegate.IM_Invoke%%o<DesktopButton> + 60
7   preemptivestring              	       0x1050c7a58 AddHandler.Stub.20%% + 56
8   AppKit                        	       0x1a1ddf53c -[NSApplication(NSResponder) sendAction:to:from:] + 460
9   AppKit                        	       0x1a1ddf340 -[NSControl sendAction:to:] + 72
10  AppKit                        	       0x1a1ddf284 __26-[NSCell _sendActionFrom:]_block_invoke + 100
11  AppKit                        	       0x1a1ddf1ac -[NSCell _sendActionFrom:] + 204
12  AppKit                        	       0x1a1ddf0a8 -[NSButtonCell _sendActionFrom:] + 96
13  AppKit                        	       0x1a1ddc630 NSControlTrackMouse + 1480
14  AppKit                        	       0x1a1ddc03c -[NSCell trackMouse:inRect:ofView:untilMouseUp:] + 144
15  AppKit                        	       0x1a1ddbeb4 -[NSButtonCell trackMouse:inRect:ofView:untilMouseUp:] + 580
16  AppKit                        	       0x1a1ddb338 -[NSControl mouseDown:] + 448
17  XojoFramework                 	       0x105e76be0 0x105d8c000 + 961504
18  AppKit                        	       0x1a1dda1d8 -[NSWindow(NSEventRouting) _handleMouseDownEvent:isDelayedEvent:] + 3672
19  AppKit                        	       0x1a1d65d2c -[NSWindow(NSEventRouting) _reallySendEvent:isDelayedEvent:] + 384
20  AppKit                        	       0x1a1d659dc -[NSWindow(NSEventRouting) sendEvent:] + 284
21  XojoFramework                 	       0x105e8306c 0x105d8c000 + 1011820
22  AppKit                        	       0x1a257b1a8 -[NSApplication(NSEventRouting) sendEvent:] + 1656
23  XojoFramework                 	       0x105e72b64 0x105d8c000 + 944996
24  preemptivestring              	       0x10500a42c DesktopApplication._CallFunctionWithExceptionHandling%%o<DesktopApplication>p + 164
25  XojoFramework                 	       0x105ff99f0 CallFunctionWithExceptionHandling(void (*)()) + 180
26  XojoFramework                 	       0x105e72b14 0x105d8c000 + 944916
27  AppKit                        	       0x1a2189a14 -[NSApplication _handleEvent:] + 60
28  AppKit                        	       0x1a1c318b4 -[NSApplication run] + 520
29  XojoFramework                 	       0x105ff8170 RuntimeRun + 60
30  preemptivestring              	       0x105041238 REALbasic._RuntimeRun + 28
31  preemptivestring              	       0x1050cc2c4 _Main + 476
32  preemptivestring              	       0x1050cbbd0 main + 36
33  dyld                          	       0x19dc94274 start + 2840

Submitted as https://tracker.xojo.com/xojoinc/xojo/-/issues/77525

If i protect access to foobar with a critical section, i see no crashes on M1 Mac Mini. Without i see them, too.

But i think it’s no good idea to max out the main thread with processing, then the GUI blocks, as always. This should all be in other preemptive threads, no?

preemptivestring_thk.xojo_binary_project.zip (6.1 KB)

1 Like

Agree - In a real app, this wouldn’t be a good design to lock up the UI. But this is a toy app designed only to show the crash.

1 Like

With protection you see crash?

Your version does not crash, but it’s very different than my version.

My version has:

  • Main Thread + Cooperative thread, both with sleep(10)
  • Main Thread + Preemptive thread, both with sleep(10)

Your version has

  • Cooperative Thread + Preemptive thread (nothing on Main thread), with no sleeping in either thread.

So it’s really quite a different scenario.

I would. All shared items being updated by any preemptive thread must be protected as a critical section.

I did not watch our demo yet, but I will.

I suppose the question is just how preemptive the new thread type is. If assigning a string to a string property can be interrupted in at any stage (i.e., between machine instructions), then yes, writing to a shared property without first acquring that property via a semaphore, accessing the property, and then releasing the sempaphore, is very likely to lead to crashes.

My understanding is that properties are protected internally. It’s still a good idea, but shouldn’t be necessary to prevent a crash.

No known language does it. Lots of overheads. It will increase the size of the app, slows it down doing unnecessary steps, and maybe even cause crashes due to having unknown layers of internal “semaphores” out of the control of the designer.

protected_preemptive_string.zip (5.8 KB)

Just adapted, fired, waited 5 secs, and aborted.

Check if it’s ok with more care. :wink:

We need Xojo to clarify this, but here’s what they wrote:

[1]

Xojo has done a good job of making a lot of the framework safe, but that just means your app shouldn’t crash if, say, the Thread tries to manipulate a variable at the same time as other code. It’s up to you to make sure one area of your app doesn’t clobber the changes made by another.

and

[2]

What things are thread-safe?

We’ve updated our framework to safeguard areas that were previously not preemptive thread-safe. For the most part, our entire framework is now safe for concurrent use with preemptive threads, with a few exceptions like XojoScript and Runtime.IterateObjects. When sharing resources such as MemoryBlocks, Dictionaries, or Arrays among threads, you may need to use CriticalSections or Semaphores to ensure safe access. However, if you’re only reading from these resources without modifying them, synchronization is not required. You’ll be happy to know that calling Thread.AddUserInterfaceUpdate from a preemptive thread is also safe.

Links:
[1] Preemptive Threads Are Here, and They Are… Pretty Good
[2] Cooperative to Preemptive: Weaving New Threads into your Apps

1 Like

Actually Swift now does a pretty good job of it with deep support in the language, see Documentation