diff --git a/Source/ASExperimentalFeatures.h b/Source/ASExperimentalFeatures.h index e4bdf067fe..2af937d663 100644 --- a/Source/ASExperimentalFeatures.h +++ b/Source/ASExperimentalFeatures.h @@ -21,10 +21,9 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) { ASExperimentalUnfairLock = 1 << 3, // exp_unfair_lock ASExperimentalLayerDefaults = 1 << 4, // exp_infer_layer_defaults ASExperimentalNetworkImageQueue = 1 << 5, // exp_network_image_queue - ASExperimentalDeallocQueue = 1 << 6, // exp_dealloc_queue_v2 - ASExperimentalCollectionTeardown = 1 << 7, // exp_collection_teardown - ASExperimentalFramesetterCache = 1 << 8, // exp_framesetter_cache - ASExperimentalSkipClearData = 1 << 9, // exp_skip_clear_data + ASExperimentalCollectionTeardown = 1 << 6, // exp_collection_teardown + ASExperimentalFramesetterCache = 1 << 7, // exp_framesetter_cache + ASExperimentalSkipClearData = 1 << 8, // exp_skip_clear_data ASExperimentalFeatureAll = 0xFFFFFFFF }; diff --git a/Source/ASRunLoopQueue.mm b/Source/ASRunLoopQueue.mm index 391e8e85e1..0463c6221b 100644 --- a/Source/ASRunLoopQueue.mm +++ b/Source/ASRunLoopQueue.mm @@ -31,177 +31,21 @@ static void runLoopSourceCallback(void *info) { #pragma mark - ASDeallocQueue -@interface ASDeallocQueueV1 : ASDeallocQueue -@end -@interface ASDeallocQueueV2 : ASDeallocQueue -@end - -@implementation ASDeallocQueue +@implementation ASDeallocQueue { + std::vector _queue; + ASDN::Mutex _lock; +} + (ASDeallocQueue *)sharedDeallocationQueue NS_RETURNS_RETAINED { static ASDeallocQueue *deallocQueue = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - if (ASActivateExperimentalFeature(ASExperimentalDeallocQueue)) { - deallocQueue = [[ASDeallocQueueV2 alloc] init]; - } else { - deallocQueue = [[ASDeallocQueueV1 alloc] init]; - } + deallocQueue = [[ASDeallocQueue alloc] init]; }); return deallocQueue; } -- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr -{ - ASDisplayNodeFailAssert(@"Abstract method."); -} - -- (void)drain -{ - ASDisplayNodeFailAssert(@"Abstract method."); -} - -@end - -@implementation ASDeallocQueueV1 { - NSThread *_thread; - NSCondition *_condition; - std::deque _queue; - ASDN::RecursiveMutex _queueLock; -} - -- (void)releaseObjectInBackground:(id _Nullable __strong *)objectPtr -{ - if (objectPtr != NULL && *objectPtr != nil) { - ASDN::MutexLocker l(_queueLock); - _queue.push_back(*objectPtr); - *objectPtr = nil; - } -} - -- (void)threadMain -{ - @autoreleasepool { - __unsafe_unretained __typeof__(self) weakSelf = self; - // 100ms timer. No resources are wasted in between, as the thread sleeps, and each check is fast. - // This time is fast enough for most use cases without excessive churn. - CFRunLoopTimerRef timer = CFRunLoopTimerCreateWithHandler(NULL, -1, 0.1, 0, 0, ^(CFRunLoopTimerRef timer) { - weakSelf->_queueLock.lock(); - if (weakSelf->_queue.size() == 0) { - weakSelf->_queueLock.unlock(); - return; - } - // The scope below is entered while already locked. @autorelease is crucial here; see PR 2890. - __unused NSInteger count; // Prevent static analyzer warning if release build - @autoreleasepool { -#if ASRunLoopQueueLoggingEnabled - NSLog(@"ASDeallocQueue Processing: %lu objects destroyed", weakSelf->_queue.size()); -#endif - // Sometimes we release 10,000 objects at a time. Don't hold the lock while releasing. - std::deque currentQueue = weakSelf->_queue; - count = currentQueue.size(); - ASSignpostStartCustom(ASSignpostDeallocQueueDrain, self, count); - weakSelf->_queue = std::deque(); - weakSelf->_queueLock.unlock(); - currentQueue.clear(); - } - ASSignpostEndCustom(ASSignpostDeallocQueueDrain, self, count, ASSignpostColorDefault); - }); - - CFRunLoopRef runloop = CFRunLoopGetCurrent(); - CFRunLoopAddTimer(runloop, timer, kCFRunLoopCommonModes); - - [_condition lock]; - [_condition signal]; - // At this moment, -init is signalled that the thread is guaranteed to be finished starting. - [_condition unlock]; - - // Keep processing events until the runloop is stopped. - CFRunLoopRun(); - - CFRunLoopTimerInvalidate(timer); - CFRunLoopRemoveTimer(runloop, timer, kCFRunLoopCommonModes); - CFRelease(timer); - - [_condition lock]; - [_condition signal]; - // At this moment, -stop is signalled that the thread is guaranteed to be finished exiting. - [_condition unlock]; - } -} - -- (instancetype)init -{ - if ((self = [super init])) { - _condition = [[NSCondition alloc] init]; - - _thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadMain) object:nil]; - _thread.name = @"ASDeallocQueue"; - - // Use condition to ensure NSThread has finished starting. - [_condition lock]; - [_thread start]; - [_condition wait]; - [_condition unlock]; - } - return self; -} - -- (void)stop -{ - if (!_thread) { - return; - } - - [_condition lock]; - [self performSelector:@selector(_stop) onThread:_thread withObject:nil waitUntilDone:NO]; - [_condition wait]; - // At this moment, the thread is guaranteed to be finished running. - [_condition unlock]; - _thread = nil; -} - -- (void)drain -{ - [self performSelector:@selector(_drain) onThread:_thread withObject:nil waitUntilDone:YES]; -} - -- (void)_drain -{ - while (true) { - @autoreleasepool { - _queueLock.lock(); - std::deque currentQueue = _queue; - _queue = std::deque(); - _queueLock.unlock(); - - if (currentQueue.empty()) { - return; - } else { - currentQueue.clear(); - } - } - } -} - -- (void)_stop -{ - CFRunLoopStop(CFRunLoopGetCurrent()); -} - -- (void)dealloc -{ - [self stop]; -} - -@end - -@implementation ASDeallocQueueV2 { - std::vector _queue; - ASDN::Mutex _lock; -} - - (void)dealloc { ASDisplayNodeFailAssert(@"Singleton should not dealloc."); diff --git a/examples/CatDealsCollectionView/Sample/AppDelegate.m b/examples/CatDealsCollectionView/Sample/AppDelegate.m index d626041192..447ff93b88 100644 --- a/examples/CatDealsCollectionView/Sample/AppDelegate.m +++ b/examples/CatDealsCollectionView/Sample/AppDelegate.m @@ -47,14 +47,3 @@ } @end - -@implementation ASConfiguration (UserProvided) - -+ (ASConfiguration *)textureConfiguration -{ - ASConfiguration *cfg = [[ASConfiguration alloc] init]; - cfg.experimentalFeatures = ASExperimentalDeallocQueue; - return cfg; -} - -@end