Add @synchronized around UIImage draw to prevent concurrently decoding the same instance.

This is a workaround necessary due to an iOS 9 bug.  I'm not gating it to iOS 9 only so as to
not exaggerate iOS version-specific behavior differences in the framework, since a majority are
on iOS 9 anyway.  If we can confirm the bug is fixed in a later iOS version, then it will be gated.

Issue tracked in greater detail here: https://github.com/facebook/AsyncDisplayKit/issues/1068
This commit is contained in:
Scott Goodson
2016-01-23 13:35:04 -08:00
parent 2c5db2e335
commit 264887413e

View File

@@ -235,7 +235,21 @@
UIRectFill({ .size = backingSize });
}
[image drawInRect:imageDrawRect];
// iOS 9 appears to contain a thread safety regression when drawing the same CGImageRef on
// multiple threads concurrently. In fact, instead of crashing, it appears to deadlock.
// The issue is present in Mac OS X El Capitan and has been seen hanging Pro apps like Adobe Premier,
// as well as iOS games, and a small number of ASDK apps that provide the same image reference
// to many separate ASImageNodes. A workaround is to set .displaysAsynchronously = NO for the nodes
// that may get the same pointer for a given UI asset image, etc.
// FIXME: We should replace @synchronized here, probably using a global, locked NSMutableSet, and
// only if the object already exists in the set we should create a semaphore to signal waiting threads
// upon removal of the object from the set when the operation completes.
// Another option is to have ASDisplayNode+AsyncDisplay coordinate these cases, and share the decoded buffer.
// Details tracked in https://github.com/facebook/AsyncDisplayKit/issues/1068
@synchronized(image) {
[image drawInRect:imageDrawRect];
}
if (isCancelled()) {
UIGraphicsEndImageContext();