Fix potential deadlock when accessing a shared pasteboard from a background thread #310
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.
Hello there.
We're using Adjust here at Duolingo and recently discovered an interesting deadlock scenario on one of our employees' devices. Please find an example watchdog termination crash report here: https://gist.github.com/sanekgusev/d5472726ac577032526799df37da532a.
Adjust is accessing a shared pasteboard
fb_app_attribution
to retrieve Facebook attribution ID. It does so by creating aUIPasteboard
instance and reading the string data out of it. In the current implementation this appears to always happen from a background thread, shortly after initializing the SDK.The deadlock occurs when Facebook tries to create an
UIPasteboard
instance on its own, at the exact same time, but from the main thread. This could be because it is actually accessing the exact samefb_app_attribution
pasteboard, but this might be an issue for any concurrent shared pasteboard access.Looking at the crashlog, the deadlock scenario can be outlined as follows:
-[UIPasteboard pasteboardWithName: create:]
(which tail calls into a private-[UIPasteboard _pasteboardWithName: create:]
that we can see in the crashlog).-[UIPasteboard pasteboardWithName: create:]
performs a barrier sync dispatch to an internalcom.apple.UIKit.pasteboard.cache-queue
, apparently to check if the pasteboard has been previously accessed and cached.+[FBSDKAppEventsUtility attributionID]
, attempts to get access to the same shared pasteboard and also calls-[UIPasteboard pasteboardWithName: create:]
com.apple.UIKit.pasteboard.cache-queue
queue. Main thread is blocked at this point.com.apple.UIKit.pasteboard.cache-queue
, attempts to do another sync dispatch, this time to the main queue, from the_pasteboardCacheQueue_existingItemCollectionWithName
function. Deadlock.While one can find evidence of people successfully using
UIPasteboard
class from threads other than main online, there is no explicit statement from Apple declaring that the class is thread-safe. As such, just as with any other UIKit API, we should assume that it may only be safely used from the main thread. The provided deadlock scenario above is another evidence to this.If we wrap the
UIPasteboard
creation code in Adjust SDK into adispatch_sync
call and guarantee that it always runs on the main queue, we'd prevent the deadlock from happening. This PR does exactly that.