Closed
Conversation
Contributor
|
Thank you for your very comprehensive issue and reproduction case. I've created a unit test that is able to consistently reproduce the problem you uncovered, and I've written a fix for this in #274. @baksha97 please feel free to review that pull request, or see if it is able to resolve the problem in your application. |
Contributor
|
Resolved in #274 and released in version 2.1.5. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem Analysis (Technical)
The SDK's
withIsolationSyncThrowingandwithIsolationSyncfunctions (CommonSupport/ExpressionUtilities.swift) bridge synchronous code to actor-isolated code by spawning aTaskand blocking onDispatchGroup.wait(). When called from a Swift Concurrency cooperative thread — i.e. inside anyasyncfunction,Task, or actor-isolated context —group.wait()blocks that cooperative thread while the innerTaskalso needs a cooperative thread to execute (to hop to@CredentialActor). Once the pool is saturated, neither side can make progress, and the process deadlocks.The most direct path is
Credential.revoke(type:), which is itselfasyncand callswithIsolationSyncThrowingat L285 — guaranteeing the call originates on the cooperative pool. The remaining 5withIsolationSyncThrowingcall sites and 22withIsolationSynccall sites are synchronous but deadlock when any caller happens to be on the cooperative pool.Solution (Technical)
This PR provides a detailed bug report with a self-contained reproduction (
Sources/CooperativePoolDeadlock/main.swift) that exercises the real SDK modules to deterministically trigger the deadlock. Recommendation is to stick with a single consistent concurrency pattern.Affected Components
CommonSupport/ExpressionUtilities.swift—withIsolationSyncThrowing,withIsolationSyncAuthFoundation/Credential.swift— 6withIsolationSyncThrowingcall sites + 4withIsolationSynccall sitesAuthFoundation/CredentialCoordinatorImpl.swift,Authentication.swift—withIsolationSynccall sitesAuthenticationFlowconformances (AuthorizationCodeFlow,DeviceAuthorizationFlow,JWTAuthorizationFlow,ResourceOwnerFlow,SessionTokenFlow,TokenExchangeFlow,DirectAuthFlow,InteractionCodeFlow,SessionLogoutFlow) —withIsolationSyncinisAuthenticating/contextproperty gettersmasterat5c710b0Steps to reproduce:
swift build --product CooperativePoolDeadlock \ --sdk "$(xcrun --sdk iphonesimulator --show-sdk-path)" \ --triple arm64-apple-ios13.0-simulatorxcrun simctl boot "iPhone 16 Pro Max" SIMCTL_CHILD_LIBDISPATCH_COOPERATIVE_POOL_STRICT=1 \ xcrun simctl spawn --standalone booted \ .build/arm64-apple-ios-simulator/debug/CooperativePoolDeadlockActual result:
Process hangs for 5 seconds, prints
DEADLOCK CONFIRMED, and exits with code 1. Under normal pool sizing (without the env var), the Swift runtime detects thread starvation and terminates withSIGTRAPorEXC_BAD_ACCESS— matching the production Crashlytics crash oncom.apple.root.user-initiated-qos.cooperative.Expected result:
Credential.revoke(),Credential.remove(), and other Credential APIs called fromasynccontexts complete without deadlocking or crashing.Tests
Sources/CooperativePoolDeadlock/main.swift— reproduction executable that importsCommonSupportandAuthFoundation, spawns concurrentTasks callingwithIsolationSyncThrowingwith a@CredentialActor-isolated closure (mirroringCredential.revoke()→remove()), and uses a 5-second watchdog to confirm the deadlock. Exits 1 on deadlock, 0 if the pool was not saturated.