Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rollout ASDeallocQueueV2 #trivial #1143

Merged
merged 5 commits into from
Oct 1, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions Source/ASExperimentalFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
};

Expand Down
166 changes: 5 additions & 161 deletions Source/ASRunLoopQueue.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,177 +31,21 @@ static void runLoopSourceCallback(void *info) {

#pragma mark - ASDeallocQueue

@interface ASDeallocQueueV1 : ASDeallocQueue
ernestmama marked this conversation as resolved.
Show resolved Hide resolved
@end
@interface ASDeallocQueueV2 : ASDeallocQueue
@end

@implementation ASDeallocQueue
@implementation ASDeallocQueue {
std::vector<CFTypeRef> _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<id> _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<id> currentQueue = weakSelf->_queue;
count = currentQueue.size();
ASSignpostStartCustom(ASSignpostDeallocQueueDrain, self, count);
weakSelf->_queue = std::deque<id>();
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<id> currentQueue = _queue;
_queue = std::deque<id>();
_queueLock.unlock();

if (currentQueue.empty()) {
return;
} else {
currentQueue.clear();
}
}
}
}

- (void)_stop
{
CFRunLoopStop(CFRunLoopGetCurrent());
}

- (void)dealloc
{
[self stop];
}

@end

@implementation ASDeallocQueueV2 {
std::vector<CFTypeRef> _queue;
ASDN::Mutex _lock;
}

- (void)dealloc
{
ASDisplayNodeFailAssert(@"Singleton should not dealloc.");
Expand Down
11 changes: 0 additions & 11 deletions examples/CatDealsCollectionView/Sample/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,3 @@ - (void)pushNewViewController
}

@end

@implementation ASConfiguration (UserProvided)

+ (ASConfiguration *)textureConfiguration
{
ASConfiguration *cfg = [[ASConfiguration alloc] init];
cfg.experimentalFeatures = ASExperimentalDeallocQueue;
return cfg;
}

@end