Skip to content

Conversation

@gmazzo
Copy link

@gmazzo gmazzo commented Jul 25, 2025

We are observing some deadlocks in our project, mostly caused by a complex injection hierarchy. After some research, we concluded some possible injection paths can lead into it:

For instance, given the follow Components:

classDiagram
        Foundation <|-- Tracking
        Foundation <|-- Observability
        Foundation : +Logger logger
        Foundation : +Client httpClient
        Tracking : +TrackingService trackingService
        Observability : +ObservabilityService observabiltyService
Loading

If we initialize concurrently (in different threads) 2 classes that both require some injection from Tracking and Observability, but in different order, then we'll have a deadlock on both trying to acquire a lock on Foundation's:

classDiagram
ServiceA : +TrackingService trackingService
ServiceA : +ObservabilityService observabiltyService
ServiceB : +ObservabilityService observabiltyService
ServiceB : +TrackingService trackingService
Loading

We are proposing to introduce a per-injection-point lock (but still retaining the main lock in the component to mutate the sharedInstances map) to avoid this situation.

Note

This PR is based on #484, see here the real diff gmazzo/needle@simplify-component...fix-deadlock

In our real case, ServiceA and ServiceB are bootstrappingService and devicesProfilingManager. This is the crash:

CrashReporter Key:  449ce4c905518c2d2ab7c029890391d997feb07a

App Hang: The app was terminated while unresponsive

Thread 0 - com.apple.main-thread - (TH_STATE_WAITING)
0  libsystem_kernel.dylib +0x1ca4  ___psynch_mutexwait
1  libsystem_pthread.dylib +0x1d64 __pthread_mutex_firstfit_lock_wait
2  libsystem_pthread.dylib +0x17ec __pthread_mutex_firstfit_lock_slow
3  Foundation +0x4048c             -[NSRecursiveLock lock]
4  Glovo +0x2e00d14                Component.shared<A>(__function:_:)
5  Glovo +0x1884548                FraudModule.devicesProfilingManager.getter
6  Glovo +0x18877a0                protocol witness for FraudModuleProtocol.devicesProfilingManager.getter in conformance FraudModule
7  Glovo +0x133e070                closure #1 in CustomerFoundationModule.bootstrappingService.getter
8  Glovo +0x2e0114c                Component.shared<A>(__function:_:)
9  Glovo +0x133dea0                CustomerFoundationModule.bootstrappingService.getter
10 Glovo +0x133ef68                protocol witness for CustomerFoundationModuleProtocol.bootstrappingService.getter in conformance CustomerFoundationModule
11 Glovo +0x1357e4                 closure #1 in AccountModule.launchViewControllerFactory.getter
12 Glovo +0x2e0114c                Component.shared<A>(__function:_:)
13 Glovo +0x135734                 AccountModule.launchViewControllerFactory.getter
14 Glovo +0x3e2b4                  protocol witness for AccountModuleProtocol.launchViewControllerFactory.getter in conformance AccountModule (<compiler-generated>)
......


Thread 19 - com.apple.root.user-initiated-qos - (TH_STATE_WAITING)
0  libsystem_kernel.dylib +0x1ca4  ___psynch_mutexwait
1  libsystem_pthread.dylib +0x1d64 __pthread_mutex_firstfit_lock_wait
2  libsystem_pthread.dylib +0x17ec __pthread_mutex_firstfit_lock_slow
3  Foundation +0x4048c             -[NSRecursiveLock lock]
4  Glovo +0x2e00d14                Component.shared<A>(__function:_:)
5  Glovo +0x1339670                CustomerFoundationModule.observabilityService.getter
6  Glovo +0x133ee28                protocol witness for CustomerFoundationModuleProtocol.observabilityService.getter in conformance CustomerFoundationModule
7  Glovo +0x1884804                closure #1 in FraudModule.devicesProfilingManager.getter
8  Glovo +0x2e0114c                Component.shared<A>(__function:_:)
9  Glovo +0x1884548                FraudModule.devicesProfilingManager.getter
10 Glovo +0x1eba0                  protocol witness for FraudModuleProtocol.devicesProfilingManager.getter in conformance FraudModule (<compiler-generated>)
11 Glovo +0x13c31d0                TransportHeadersFactory.includeSessionHeaders(_:)
12 Glovo +0x13c1248                TransportHeadersFactory.getRequestHeaders(for:)
13 Glovo +0x13c42d8                protocol witness for TransportHeadersFactoryProtocol.getRequestHeaders(for:) in conformance TransportHeadersFactory
14 Glovo +0x1373ffc                $s18CustomerFoundation13JSONTransportC14prepareRequest33_4A31403EA63FA50A6A144345B7AE17C1LL_12retryContext0B010URLRequestV0aB3API0cE0V_AA0c5RetryO0VSgtAJ17GlovoServiceErrorCYKF
15 Glovo +0x1370528                JSONTransport.execute<A>(_:callback:)

@gmazzo gmazzo marked this pull request as ready for review July 29, 2025 15:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant