-
Notifications
You must be signed in to change notification settings - Fork 518
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GraphIdentifier threadsafety optimization & patch interleaving race condition in Lazy resolve #523
GraphIdentifier threadsafety optimization & patch interleaving race condition in Lazy resolve #523
Conversation
1467514
to
7f5382a
Compare
@@ -178,16 +178,7 @@ public final class Container { | |||
} | |||
|
|||
internal func restoreObjectGraph(_ identifier: GraphIdentifier) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be worth documenting that this method is not protected with a lock because it is expected to be called within a concurrency context
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, and if it weren't for the tests we could mark this private too. Or discard the function and just set it—since this function is only called within this file at this point. I will add a comment.
what the, why did clicking the re-request button delete the other ones… 🤯 |
We encountered a bizarre error in an async context; this PR offers a solution to two problems. (A topic of note here is that the
GraphIdentifier
is a class. We will come back to this later).EXC_BAD_ACCESS: KERN_INVALID_ADDRESS: Attempted to dereference garbage pointer
Lazy.instance
could acquire the lock, set the graph identifier, release the lock, then re-acquire the lock and begin resolving. In between the lock release/acquisition of one.instance
call, another.instance
call could update the GraphIdentifier before the first thread could callresolve
and re-acquire the lock. Can results in unexpected behaviour.Synchronization
Synchronizing a container will emit a child which ensures that any async calls to
resolve(…)
should happen sequentially and never concurrently (or more precisely, for the logic within).One line here is quite interesting:
It happens within the sync context, so it should be safe. However, there is one Swinject object that also contains a graph identifier reference. That is Lazy.
A Lazy property can be held by any class. Once that class is dereferenced/deallocated so are its children, in this case the
ObjectIdentifier
being one of them.Critically, we have no control over when a Lazy property (and its GraphIdentifier ref) might be released.
Lazy
type.Solution
This one fairly simple and results meets expectations for GraphIdentifier behaviour. It should be an Identifiable struct, the performance tradeoffs here are negligible: a smidge costlier to create, but faster to pass by value (requires no ARC calls).
Some debug artifacts…
@_assemblyVision