mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-11 20:00:30 +01:00
Some sophisticated main thread deadlock #258
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @iby on GitHub (Jan 16, 2019).
I feel like have discovered a treasure and can't figure out where this is coming from… Everything worked fine yesterday, rolling to earlier commits doesn't help nor deleting all the data. I've narrowed this down to the following block:
More specifically to the logger, which accepts
infomessage as@autoclosureand does this:One problem I see is that the message gets acquired within that lock, which might lead to all sort of troubles. I'll report this anyway, but this doesn't seem to be the main issue here.
Another message being logged later in the code on the main thread fails to acquire that lock, because it's still waiting on the
fetchCount.Now, what is strange about this, if I do prefetch or dump core store string (either one will do) just prior logging, everything works fine without causing a deadlock:
This makes me think there's some initialization going on (for a particular thread?) or internal CoreData bug or both, as the execution stops at the
[NSManagedObjectContext performBlockAndWait:]line:Is this something you came across or can recommend the best way around this problem? Updating the logger locking fixes this, but there's a deeper issue, which might occur elsewhere. I'm trying to understand and foresee such cases. 👍
P. S. And it worked perfectly fine up until today! I feel like looking at a ghost! What are the odds…
@JohnEstropia commented on GitHub (Jan 17, 2019):
Not at all surprising for a couple of reasons:
One, always remember that the

DataStackexpects to runs things on the main thread. In fact, if you're on a DEBUG build it should raise an assertion here:By design, any fetches from the
DataStackshould be done from the main queue or the main thread, otherwise all bets are off.Two, if I remember correctly
DispatchQueue.global()creates a concurrent queue, which means all assumptions about which threads they run will be wrong. I see Willow usesNSRecursionLock, which ALLOWS lock reentry iflock()is called from similar threads.@JohnEstropia commented on GitHub (Jan 17, 2019):
Regarding DataStack having main-thread affinity, it is always better to create a floating transaction if you really need to fetch something from an uncontrolled (ex: concurrent) queue:
Or better yet, if the data to be fetched is static then just fetch them outside the closure:
@iby commented on GitHub (Jan 17, 2019):
Great, thanks for the detailed response! So, am I correct to think that if one simply wants an asynchronous fetch of any kind then
perform(asynchronous: …)would be a better way to do this all together?@JohnEstropia commented on GitHub (Jan 18, 2019):
@ianbytchek
perform(asynchronous:...)would be the safest, but it would also wait for current running transactions so you might not want that if you have long running imports. So unless you want that and if you are fetching just once (that is, unaffected by race conditions), using a quick fetch with.beginUnsafe()would be fine.@JohnEstropia commented on GitHub (Jan 18, 2019):
In the future we might benefit from built-in asynchronously-fetching methods, but that's another feature for another day :)
@iby commented on GitHub (Jan 18, 2019):
Fantastic. Thanks for the details!