Create a new dealloc queue that is more efficient (#931)

* Fork dealloc queue in an experiment

* Fix and put back

* Use the right selector

* Go simpler

* Clarify name

* Type inference

* Use CFTypeRefs like a boss

* Improve comments

* License header
This commit is contained in:
Adlai Holler
2018-05-22 09:06:24 -07:00
committed by GitHub
parent b2e5f9ec64
commit 8b890f07be
8 changed files with 107 additions and 25 deletions

View File

@@ -39,23 +39,41 @@ static void runLoopSourceCallback(void *info) {
#pragma mark - ASDeallocQueue
@implementation ASDeallocQueue {
NSThread *_thread;
NSCondition *_condition;
std::deque<id> _queue;
ASDN::RecursiveMutex _queueLock;
}
@interface ASDeallocQueueV1 : ASDeallocQueue
@end
@interface ASDeallocQueueV2 : ASDeallocQueue
@end
@implementation ASDeallocQueue
+ (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED
{
static ASDeallocQueue *deallocQueue = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
deallocQueue = [[ASDeallocQueue alloc] init];
if (ASActivateExperimentalFeature(ASExperimentalDeallocQueue)) {
deallocQueue = [[ASDeallocQueueV2 alloc] init];
} else {
deallocQueue = [[ASDeallocQueueV1 alloc] init];
}
});
return deallocQueue;
}
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
{
ASDisplayNodeFailAssert(@"Abstract method.");
}
@end
@implementation ASDeallocQueueV1 {
NSThread *_thread;
NSCondition *_condition;
std::deque<id> _queue;
ASDN::RecursiveMutex _queueLock;
}
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
{
if (objectPtr != NULL && *objectPtr != nil) {
@@ -147,12 +165,12 @@ static void runLoopSourceCallback(void *info) {
_thread = nil;
}
- (void)test_drain
- (void)drain
{
[self performSelector:@selector(_test_drain) onThread:_thread withObject:nil waitUntilDone:YES];
[self performSelector:@selector(_drain) onThread:_thread withObject:nil waitUntilDone:YES];
}
- (void)_test_drain
- (void)_drain
{
while (true) {
@autoreleasepool {
@@ -182,6 +200,57 @@ static void runLoopSourceCallback(void *info) {
@end
@implementation ASDeallocQueueV2 {
std::vector<CFTypeRef> _queue;
ASDN::Mutex _lock;
}
- (void)dealloc
{
ASDisplayNodeFailAssert(@"Singleton should not dealloc.");
}
- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr
{
NSParameterAssert(objectPtr != NULL);
// Cast to CFType so we can manipulate retain count manually.
auto cfPtr = (CFTypeRef *)(void *)objectPtr;
if (!cfPtr || !*cfPtr) {
return;
}
_lock.lock();
auto isFirstEntry = _queue.empty();
// Push the pointer into our queue and clear their pointer.
// This "steals" the +1 from ARC and nils their pointer so they can't
// access or release the object.
_queue.push_back(*cfPtr);
*cfPtr = NULL;
_lock.unlock();
if (isFirstEntry) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.100 * NSEC_PER_SEC)), dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0), ^{
[self drain];
});
}
}
- (void)drain
{
@autoreleasepool {
_lock.lock();
auto q = std::move(_queue);
_lock.unlock();
for (auto ref : q) {
// NOTE: Could check that retain count is 1 and retry later if not.
CFRelease(ref);
}
}
}
@end
#if AS_KDEBUG_ENABLE
/**
* This is real, private CA API. Valid as of iOS 10.