From a7bb8a3f01b8eef5a928b1b952738ae931ad8692 Mon Sep 17 00:00:00 2001 From: ap-harsa-yamani Date: Thu, 20 Aug 2026 16:03:29 +0700 Subject: [PATCH] fix: remove deadlock in pt_usleep sleep-spam blocker The sleep-spam blocker locked an NSLock inside dispatch_once and never unlocked it, then tried to lock it again when a thread exceeded the usleep limit. NSLock is not recursive, so the second lock deadlocks the thread forever instead of stopping it. If the hung thread is a render/GPU thread (UnityGfxDeviceWorker) or the main thread, this corrupts os_unfair_lock and crashes the game (observed in Arknights: Endfield). Replace the deadlocking lock with a simple return 0 (skip the sleep), which stops the spam without hanging the thread. Remove the now-unneeded UIApplicationWillTerminateNotification unlock observer. --- PlayTools/PlayLoader.m | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/PlayTools/PlayLoader.m b/PlayTools/PlayLoader.m index 85e45905..5e30cdd9 100644 --- a/PlayTools/PlayLoader.m +++ b/PlayTools/PlayLoader.m @@ -331,14 +331,11 @@ static int pt_unlink(char const* path) { static NSMutableDictionary *thread_sleep_counters = nil; static NSMutableDictionary *last_sleep_attempts = nil; static dispatch_once_t thread_sleep_once; -static NSLock *thread_sleep_lock = nil; static int pt_usleep(useconds_t time) { dispatch_once(&thread_sleep_once, ^{ thread_sleep_counters = [NSMutableDictionary dictionary]; last_sleep_attempts = [NSMutableDictionary dictionary]; - thread_sleep_lock = [[NSLock alloc] init]; - [thread_sleep_lock lock]; }); if ([[PlaySettings shared] blockSleepSpamming]) { @@ -367,14 +364,10 @@ static int pt_usleep(useconds_t time) { } if (exceeded_sleep_limit) { - // Stop this thread from spamming usleep calls - NSLog(@"[PC] Thread %i exceeded usleep limit. Seem sus, stopping this " - @"thread FOREVER", - thread_id); - - [thread_sleep_lock lock]; - [thread_sleep_lock unlock]; - + // Stop this thread from spamming usleep calls by skipping the sleep. + // Do NOT block/hang the thread: hanging a render/GPU thread here is what + // caused os_unfair_lock corruption and crashes in Unity games (e.g. Endfield). + NSLog(@"[PC] Thread %i exceeded usleep limit. Skipping its sleeps.", thread_id); return 0; } } @@ -404,16 +397,6 @@ static void __attribute__((constructor)) initialize(void) { if (ue_status == 2) { [PlayKeychain debugLogger: [NSString stringWithFormat:@"UnrealEngine Hooked"]]; } - - if ([[PlaySettings shared] blockSleepSpamming]) { - // Add an observer so we can unlock threads on app termination - [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillTerminateNotification - object:nil - queue:[NSOperationQueue mainQueue] - usingBlock:^(NSNotification * _Nonnull note) { - [thread_sleep_lock unlock]; - }]; - } } @end