Skip to content

fix: remove deadlock in pt_usleep sleep-spam blocker (can hang render thread → os_unfair_lock crash) - #234

Open
ap-harsa-yamani wants to merge 1 commit into
PlayCover:masterfrom
ap-harsa-yamani:fix/pt_usleep-deadlock
Open

fix: remove deadlock in pt_usleep sleep-spam blocker (can hang render thread → os_unfair_lock crash)#234
ap-harsa-yamani wants to merge 1 commit into
PlayCover:masterfrom
ap-harsa-yamani:fix/pt_usleep-deadlock

Conversation

@ap-harsa-yamani

Copy link
Copy Markdown

Summary

Fixes a deadlock in the pt_usleep sleep-spam blocker (blockSleepSpamming toggle) that can hang a thread forever — and, when the hung thread is a render/GPU thread, corrupts os_unfair_lock and crashes the game.

The bug

In PlayTools/PlayLoader.m, pt_usleep:

static int pt_usleep(useconds_t time) {
    dispatch_once(&thread_sleep_once, ^{
        ...
        thread_sleep_lock = [[NSLock alloc] init];
        [thread_sleep_lock lock];      // (1) LOCKED inside dispatch_once, never unlocked
    });

    if ([[PlaySettings shared] blockSleepSpamming]) {
        ...
        if (exceeded_sleep_limit) {     // >100× usleep(100000) within 2s
            [thread_sleep_lock lock];   // (2) NSLock is NOT recursive → DEADLOCK forever
            [thread_sleep_lock unlock]; //     never reached
            return 0;                   //     never reached
        }
    }
    return usleep(time);
}

The fix

  • Remove the deadlocking thread_sleep_lock entirely. The per-thread counter is already protected by @synchronized(thread_sleep_counters).
  • When a thread exceeds the usleep limit, skip its sleeps (return 0) instead of hanging it — same anti-spam effect, no deadlock.
  • Remove the now-unneeded UIApplicationWillTerminateNotification observer that unlocked the (now removed) lock.

Testing

  • Brace/paren balance verified; the change is purely deletion of the lock + its observer.
  • Could not run a full xcodebuild here (no iOS SDK toolchain available in this environment); the diff is minimal and self-contained.
  • Recommended verification: build PlayTools, install, enable blockSleepSpamming in a Unity game (e.g. Endfield), and confirm the game no longer hangs/crashes after ~60s.

Related

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.
@TheMoonThatRises

Copy link
Copy Markdown
Member

@ohaiibuzzle not sure what the original design intention was for the lock system was

@TheMoonThatRises TheMoonThatRises added the ai-pr Pull request generated with AI (with or without assistance) label Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-pr Pull request generated with AI (with or without assistance)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants