From 12cceed8bcb95210768412d89cb46dd23e282c49 Mon Sep 17 00:00:00 2001 From: Stepan Tolksdorf Date: Sun, 29 Sep 2024 17:17:19 +0200 Subject: [PATCH] Fix deadlock running tests in Xcode 16 Fixes #56 --- STULabel/STUMainScreenProperties.h | 5 ++++ STULabel/STUMainScreenProperties.m | 41 +++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 9 deletions(-) diff --git a/STULabel/STUMainScreenProperties.h b/STULabel/STUMainScreenProperties.h index ad65f0f..373894d 100644 --- a/STULabel/STUMainScreenProperties.h +++ b/STULabel/STUMainScreenProperties.h @@ -14,6 +14,11 @@ STU_DISABLE_CLANG_WARNING("-Wunguarded-availability") STU_REENABLE_CLANG_WARNING }; +/// This function is normally executed automatically when the library is loaded from an Objective C @c load initialization method. +/// If this leads to issues, you can define an @c STULabel_NoMainScreenPropertiesInitializationOnLoad environment variable +/// and then call this function explicity from the main thread before using any of the STULabel functionality. +void stu_initializeMainScreenProperties(void); + /// Returns the value of @c UIScreen.main.fixedCoordinateSpace.bounds.size. /// Thread-safe. CGSize stu_mainScreenPortraitSize(void); diff --git a/STULabel/STUMainScreenProperties.m b/STULabel/STUMainScreenProperties.m index 1b3f7b3..01135f8 100644 --- a/STULabel/STUMainScreenProperties.m +++ b/STULabel/STUMainScreenProperties.m @@ -57,15 +57,12 @@ static void updateMainScreenProperties(void) { #undef store } -@interface UIScreen (STUMainScreenProperties) -+ (void)load; -@end -@implementation UIScreen (STUMainScreenProperties) -+ (void)load { - // We can't do this initialization lazily, because UIScreen must only be accessed on the - // main thread. (Using `dispatch_sync(dispatch_get_main_queue(), ...)` would lead to a - // deadlock when the main thread is waiting for the thread in which stu_mainScreen... is called - // for the first time.) +void stu_initializeMainScreenProperties(void) { + static bool initialized = false; + if (initialized) { + return; + } + initialized = true; updateMainScreenProperties(); #if !STU_MAIN_SCREEN_PROPERTIES_ARE_CONSTANT NSNotificationCenter * const notificationCenter = NSNotificationCenter.defaultCenter; @@ -81,6 +78,32 @@ + (void)load { usingBlock:updateMainScreenPropertiesBlock]; #endif } + +@interface UIScreen (STUMainScreenProperties) ++ (void)load; +@end +@implementation UIScreen (STUMainScreenProperties) ++ (void)load { + // We can't do this initialization lazily, because UIScreen must only be accessed on the + // main thread. (Using `dispatch_sync(dispatch_get_main_queue(), ...)` would lead to a + // deadlock when the main thread is waiting for the thread in which stu_mainScreen... is called + // for the first time.) + // However, when executing this `load` method when a test bundle is loaded, + // calling stu_initializeMainScreenProperties synchronously leads to a deadlock in UIScreen.mainScreen, + // so for that special case we just invoke stu_initializeMainScreenProperties asynchronously, + // relying on the main screen properties not beeing used before stu_initializeMainScreenProperties has run. + NSDictionary* env = NSProcessInfo.processInfo.environment; + if ([env valueForKey:@"STULabel_NoMainScreenPropertiesInitializationOnLoad"]) { + return; + } + if ([env valueForKey:@"XCTestBundlePath"]) { + dispatch_async(dispatch_get_main_queue(), ^{ + stu_initializeMainScreenProperties(); + }); + return; + } + stu_initializeMainScreenProperties(); +} @end #if STU_MAIN_SCREEN_PROPERTIES_ARE_CONSTANT