Changing Mac Light/Dark theme triggers START_JUCE_APPLICATION() and crash

If my application is open when changing my Mac’s Light/Dark theme it triggers START_JUCE_APPLICATION() in main.cpp and then crashes. For my Mac users that have their theme set to “auto”, which automatically changes the theme at certain times of the day, this has been causing a crash if the app is open at that time.

I’m testing on Mac Sonoma, with Juce 7.0.9. Here’s the stack trace:

Thread 0 Crashed:: JUCE Message Thread Dispatch queue:
0   ???                           	               0x0 ???
1   CoreFoundation                	       0x183c868ec ___CFXRegistrationPost_block_invoke + 88
2   CoreFoundation                	       0x183c86834 _CFXRegistrationPost + 440
3   CoreFoundation                	       0x183bc148c _CFXNotificationPost + 764
4   CoreFoundation                	       0x183bf28fc CFNotificationCenterPostNotificationWithOptions + 136
5   SkyLight                      	       0x1894d1a20 post_coordinated_distributed_notification(CGSNotificationType, void*, unsigned int, void*) + 264
6   SkyLight                      	       0x18922bd14 CGSPostLocalNotification + 188
7   SkyLight                      	       0x18922b8f0 (anonymous namespace)::notify_datagram_handler(unsigned int, CGSDatagramType, void*, unsigned long, void*) + 116
8   SkyLight                      	       0x1895be1f8 CGSDatagramReadStream::dispatchMainQueueDatagrams() + 228
9   SkyLight                      	       0x1895be0f4 invocation function for block in CGSDatagramReadStream::mainQueueWakeup() + 28
10  libdispatch.dylib             	       0x183971cb8 _dispatch_call_block_and_release + 32
11  libdispatch.dylib             	       0x183973910 _dispatch_client_callout + 20
12  libdispatch.dylib             	       0x183981fa8 _dispatch_main_queue_drain + 984
13  libdispatch.dylib             	       0x183981bc0 _dispatch_main_queue_callback_4CF + 44
14  CoreFoundation                	       0x183c3f15c __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 16
15  CoreFoundation                	       0x183bfca80 __CFRunLoopRun + 1996
16  CoreFoundation                	       0x183bfbc5c CFRunLoopRunSpecific + 608
17  HIToolbox                     	       0x18e175448 RunCurrentEventLoopInMode + 292
18  HIToolbox                     	       0x18e175284 ReceiveNextEventCommon + 648
19  HIToolbox                     	       0x18e174fdc _BlockUntilNextEventMatchingListInModeWithFilter + 76
20  AppKit                        	       0x1873d6c54 _DPSNextEvent + 660
21  AppKit                        	       0x187bacebc -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 716
22  AppKit                        	       0x1873ca100 -[NSApplication run] + 476
23  Syntorial                     	       0x104b176b0 juce::MessageManager::runDispatchLoop() + 36 ( [inlined]
24  Syntorial                     	       0x104b176b0 juce::JUCEApplicationBase::main() + 144 (juce_ApplicationBase.cpp:265) [inlined]
25  Syntorial                     	       0x104b176b0 juce::JUCEApplicationBase::main(int, char const**) + 196 (juce_ApplicationBase.cpp:243) [inlined]
26  Syntorial                     	       0x104b176b0 main + 236 (Main.cpp:128)
27  dyld                          	       0x1837a50e0 start + 2360

Since this crash doesn’t involve any code that I’ve added to the JUCE-generated portion of my app, I figured this would happen with other Juce apps, so I tested with Projucer itself. But when changing the light/dark theme while Projucer is open, START_JUCE_APPLICATION() does not get triggered and thus no crash.

Any ideas? Is there a setting in Projucer, or maybe in the plist that I need to change to prevent JUCE’s START_JUCE_APPLICATION() from triggering upon theme change?

Any theories?

START_JUCE_APPLICATION() is just a macro generating the main() function, which I’m only seeing once in your stack trace at the bottom.

So nothing obviously wrong is visible, and I also cannot reproduce such a crash with a simple JUCE application.

I can see you have a SkyLight library loaded, probably doing some network communication. Maybe this is the culprit?

In any case if you have reason to believe that the crash happens in the JUCE framework and can produce a simple repro, we’ll take a closer look.

Oh I see now the main() isn’t being triggered by the theme change. My mistake.

My customers who are experiencing this also show SkyLight in the stack trace. Apparently it’s just part of the MacOS WindowServer though.

The crash is being caused by the system attempting to callback to a missing observer. The missing observer in question is one added by Desktop::NativeDarkModeChangeDetectorImpl. If I comment out:

observer.emplace (delegate.get(),
                          [NSDistributedNotificationCenter class]);

Then the crash no longer happens.

Only problem is, the JUCE Demo also adds this Observer, yet doesn’t crash when changing themes.

I don’t call any of JUCE’s Desktop DarkMode-related methods in my code.

Is there any reason why this observer would be deleted?

Just looking at the code I’m not spotting the mistake yet.

The NativeDarkModeChangeDetectorImpl ultimately lives in a global variable that is deleted after JUCEApplicationBase::shutdown() is called. So this should only happen once and after shutdown has been initiated.

Even then the observer is first removed from the Notification Center and then deleted, so it shouldn’t cause a crash anyhow.

You could add breakpoints in NativeDarkModeChangeDetectorImpl::NativeDarkModeChangeDetectorImpl() to see when this object is created, and to ~NativeDarkModeChangeDetectorImpl() to see when it’s deleted.

Inside the DelegateClass you could add a custom dealloc with a breakpoint
addMethod (@selector (dealloc), [] (id self, SEL) { auto breakPointHere = 5; });

and see when the observer instance is deleted. It should only be once after shutdown() has been called and always from ~NativeDarkModeChangeDetectorImpl().

Maybe you can spot something out of place.

Thanks for these suggestions. I tried the various breakpoints.

NativeDarkModeChangeDetectorImpl is created when the app starts up. It’s deconstructor isn’t called before changing themes.

The custom dealloc isn’t called before changing themes either.

I’ve scoured my code for anything that may inadvertently be causing this issue but it’s aimless guesswork and hasn’t yielded any clues either.

I’ll just keep the observer.emplace code commented out since I don’t utilize the system’s dark/light mode. If I stumble across the actual cause and it can be reproduced I’ll let you know.

Thanks for your help.