Ahhhh: Passing argument of non-sendable type 'OpenDocumentAction' outside of main actor-isolated context may introduce data races

Seemingly out of the blue I'm seeing issues with concurrency. Please help me to understand what I'm doing wrong. Incredibly, the example code from Apple also exhibits the same error when using the openDocument environment variable (openDocument | Apple Developer Documentation).

I don't get this warning with Xcode 16 beta 1. What Swift/Xcode version do you use?

I'm using Xcode 15.4 on macOS 14.4.1, swift version is 5.

xcrun swift --version

swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)

Target: arm64-apple-macosx14.0

It's very strange as it just started appearing out of nowhere. Can build and run app but the app will crash when the Task is run

Here is a screenshot from my code showing the crash when the openDocument is executed.

Can you try Xcode 16 beta 1? It should no longer cause any issues.

The crash is weird though. Do you have a crash log? You can detach the debugger from Xcode and then this should create a crash log after a while that you can find through the Console app.

Because I'm new here I cannot post large replies. I did however do some investigating. On my other machine which is an intel I created a new macOS App (not document based app) project (SwiftUI). I simply copy this code into the project without any change to the boiler plate or @main and I see the warning below that I see in my app:

Here is the crashlog from my app.

Process:               myApp [5579]
Path:                  /Users/USER/Library/Developer/Xcode/DerivedData/myApp-gefynztwzgskegdgtzslenommwst/Build/Products/Debug/myApp.app/Contents/MacOS/myApp
Identifier:            com.revoked.myApp
Version:               1.0 (1)
Code Type:             ARM-64 (Native)
Parent Process:        launchd [1]
User ID:               501

Date/Time:             2024-06-13 12:59:45.5615 +1000
OS Version:            macOS 14.4.1 (23E224)
Report Version:        12
Anonymous UUID:        EE8B1269-0A8A-3AB6-516B-C752E8A18B5A

Sleep/Wake UUID:       4072C134-BEE8-4BE1-AF05-92C37365EFF7

Time Awake Since Boot: 19000 seconds
Time Since Wake:       118 seconds

System Integrity Protection: enabled

Crashed Thread:        4

Exception Type:        EXC_BREAKPOINT (SIGTRAP)
Exception Codes:       0x0000000000000001, 0x0000000190cb09d4

Termination Reason:    Namespace SIGNAL, Code 5 Trace/BPT trap: 5
Terminating Process:   exc handler [5579]

Application Specific Information:
Must only be used from the main thread


Thread 0::  Dispatch queue: com.apple.main-thread
0   libsystem_kernel.dylib        	       0x18c6d21f4 mach_msg2_trap + 8
1   libsystem_kernel.dylib        	       0x18c6e4b24 mach_msg2_internal + 80
2   libsystem_kernel.dylib        	       0x18c6dae34 mach_msg_overwrite + 476
3   libsystem_kernel.dylib        	       0x18c6d2578 mach_msg + 24
4   CoreFoundation                	       0x18c7f2058 __CFRunLoopServiceMachPort + 160
5   CoreFoundation                	       0x18c7f091c __CFRunLoopRun + 1208
6   CoreFoundation                	       0x18c7efe0c CFRunLoopRunSpecific + 608
7   HIToolbox                     	       0x196f8b000 RunCurrentEventLoopInMode + 292
8   HIToolbox                     	       0x196f8ae3c ReceiveNextEventCommon + 648
9   HIToolbox                     	       0x196f8ab94 _BlockUntilNextEventMatchingListInModeWithFilter + 76
10  AppKit                        	       0x190048970 _DPSNextEvent + 660
11  AppKit                        	       0x19083adec -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 700
12  AppKit                        	       0x19003bcb8 -[NSApplication run] + 476
13  AppKit                        	       0x190012f54 NSApplicationMain + 880
14  SwiftUI                       	       0x1b80d91e0 0x1b7fcf000 + 1090016
15  SwiftUI                       	       0x1b883e8ec 0x1b7fcf000 + 8845548
16  SwiftUI                       	       0x1b8c38a5c 0x1b7fcf000 + 13015644
17  myApp               	       0x104dad7dc static myAppApp.$main() + 40
18  myApp               	       0x104dadccc main + 12 (myAppApp.swift:13)
19  dyld                          	       0x18c38a0e0 start + 2360

Thread 1:
0   libsystem_kernel.dylib        	       0x18c6d21f4 mach_msg2_trap + 8
1   libsystem_kernel.dylib        	       0x18c6e4b24 mach_msg2_internal + 80
2   libsystem_kernel.dylib        	       0x18c6dae34 mach_msg_overwrite + 476
3   libsystem_kernel.dylib        	       0x18c6d2578 mach_msg + 24
4   CoreMIDI                      	       0x1a68e4094 XServerMachPort::ReceiveMessage(int&, void*, int&) + 104
5   CoreMIDI                      	       0x1a68f557c MIDIProcess::MIDIInPortThread::Run() + 156
6   CoreMIDI                      	       0x1a68f2908 CADeprecated::XThread::RunHelper(void*) + 48
7   CoreMIDI                      	       0x1a68f45cc CADeprecated::CAPThread::Entry(CADeprecated::CAPThread*) + 92
8   libsystem_pthread.dylib       	       0x18c712f94 _pthread_start + 136
9   libsystem_pthread.dylib       	       0x18c70dd34 thread_start + 8

Thread 2::  Dispatch queue: com.example.midi.synccheck
0   libsystem_kernel.dylib        	       0x18c6d57e8 __semwait_signal + 8
1   libsystem_c.dylib             	       0x18c5b6274 nanosleep + 220
2   Foundation                    	       0x18d99ab84 +[NSThread sleepForTimeInterval:] + 160
3   myApp               	       0x104e0e3d8 closure #1 in revokedMIDIManager.startSyncCheck() + 264 (revokedMidiManager.swift:131)
4   myApp               	       0x104e0e40c thunk for @escaping @callee_guaranteed @Sendable () -> () + 48
5   libdispatch.dylib             	       0x18c560750 _dispatch_call_block_and_release + 32
6   libdispatch.dylib             	       0x18c5623e8 _dispatch_client_callout + 20
7   libdispatch.dylib             	       0x18c569a14 _dispatch_lane_serial_drain + 748
8   libdispatch.dylib             	       0x18c56a544 _dispatch_lane_invoke + 380
9   libdispatch.dylib             	       0x18c5752d0 _dispatch_root_queue_drain_deferred_wlh + 288
10  libdispatch.dylib             	       0x18c574b44 _dispatch_workloop_worker_thread + 404
11  libsystem_pthread.dylib       	       0x18c70f00c _pthread_wqthread + 288
12  libsystem_pthread.dylib       	       0x18c70dd28 start_wqthread + 8

Thread 3:
0   libsystem_pthread.dylib       	       0x18c70dd20 start_wqthread + 0

Thread 4 Crashed:
0   AppKit                        	       0x190cb09d4 -[NSWMWindowCoordinator performTransactionUsingBlock:] + 724
1   AppKit                        	       0x190b11414 -[NSWindow(NSWMWindowManagement) window:didUpdateWithChangedProperties:] + 104
2   WindowManagement              	       0x24a90d674 -[_WMWindow performUpdatesUsingBlock:] + 116
3   WindowManagement              	       0x24a90c7a8 -[_WMWindow applyTags:mask:] + 712
4   WindowManagement              	       0x24a90cca0 -[_WMWindow setVisible:] + 84
5   AppKit                        	       0x190142d7c -[NSWindow _setVisible:] + 268
6   AppKit                        	       0x190142c3c -[NSWindow _makeKeyRegardlessOfVisibility] + 40
7   AppKit                        	       0x19013b32c -[NSWindow makeKeyAndOrderFront:] + 24
8   QuickLookUI                   	       0x1eb2a147c -[QLSeamlessDocumentOpener showWindow:contentFrame:withBlock:] + 184
9   AppKit                        	       0x1902140a4 -[NSWindowController showWindow:] + 496
10  SwiftUI                       	       0x1b8e26b00 0x1b7fcf000 + 15039232
11  SwiftUI                       	       0x1b8e33380 0x1b7fcf000 + 15090560
12  AppKit                        	       0x1901c35fc -[NSDocument showWindows] + 152
13  AppKit                        	       0x1905d5b7c __102-[NSDocumentController _openDocumentWithContentsOfURL:requestedBySourceApp:display:completionHandler:]_block_invoke + 248
14  AppKit                        	       0x1905d45f4 -[NSDocumentController _coordinateReadingAndGetAlternateContentsForOpeningDocumentAtURL:resolvingSymlinks:thenContinueOnMainThreadWithAccessor:] + 132
15  AppKit                        	       0x1905d59c0 -[NSDocumentController _openDocumentWithContentsOfURL:requestedBySourceApp:display:completionHandler:] + 360
16  SwiftUI                       	       0x1b8e58b5c 0x1b7fcf000 + 15244124
17  SwiftUI                       	       0x1b86c05ad 0x1b7fcf000 + 7280045
18  myApp               	       0x104d992ad closure #1 in closure #3 in ExistingProjectButton.body.getter + 1 (NewProjectButtons.swift:50)
19  myApp               	       0x104da4ee9 partial apply for closure #1 in closure #3 in ExistingProjectButton.body.getter + 1
20  myApp               	       0x104da567d thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) + 1
21  myApp               	       0x104da57d9 partial apply for thunk for @escaping @callee_guaranteed @Sendable @async () -> (@out A) + 1
22  libswift_Concurrency.dylib    	       0x252e070f9 completeTaskWithClosure(swift::AsyncContext*, swift::SwiftError*) + 1

View protocol isn’t isolated to main actor before Xcode 16 beta, only body property, then Button’s action closure also not isolated, and Environment IIRC isolated to main actor, and (probably) via legacy behavior makes openDocument isolated. A bit of a complexity :sweat_smile:

I suggest to do 2 things:

  1. Isolate view to main actor such as @MainActor OpenDocumentButton…. With Xcode 16 View protocol finally had become isolated to main actor initially, but before that still this is good to use approach.
  2. Move out button’s action as method on this view, that you’ll then call from it instead. That will make guarantee that code (and Task) called from the main actor regardless of other details.

That should fix issues you are facing.

1 Like

I think the problem is that the async method is not @MainActor on SDKs before macOS 15.

By default nonisolated async functions are put on one of the global executes. I'm not aware of an API that can change that behavior except Task Executor Preference but that hasn't shipped yet according to the proposal.

1 Like

I think I have revised my code correctly according to your feedback. However I am still seeing the warning. Also, it may be that I'm interpreting the warning message incorrectly, it is specifically discussing 'OpenDocumentAction' as the issue.

The strange thing is that this is a warning, not an error and hasn't caused a crash until recently. Also that the warning only appeared recently. Based off this thread and others it seems it's specific to me as others cannot replicate the behaviour it seems.

Funnily enough in my actual project pulling out the code into it's own function and specifying the parent view as @MainActor worked.

Thank you both for all your help.

@dnadoba has made a great point that OpenDocumentAction hasn’t been isolated to main actor until Xcode 16. I’ve checked that as well before, but it shown as isolated to main actor without changes, so I assumed it was so before Xcode 16. I think with some tricks like proper nonisolated marks, might be with unsafe, or even preconcurrency marks it can be fixed (strange that it has gone in your main project to be honest). I can’t launch Xcode 15 on new beta macOS for some reason, so cannot check details.