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 2 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
141 changes: 1 addition & 140 deletions Source/ASRunLoopQueue.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ 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

Expand All @@ -43,11 +41,7 @@ + (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 = [[ASDeallocQueueV2 alloc] init];
});
return deallocQueue;
}
Expand All @@ -64,139 +58,6 @@ - (void)drain

@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;
Expand Down
2 changes: 1 addition & 1 deletion examples/CatDealsCollectionView/Sample/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ @implementation ASConfiguration (UserProvided)
+ (ASConfiguration *)textureConfiguration
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not just remove the whole Category here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

{
ASConfiguration *cfg = [[ASConfiguration alloc] init];
cfg.experimentalFeatures = ASExperimentalDeallocQueue;
cfg.experimentalFeatures = ASExperimentalTextNode;
ernestmama marked this conversation as resolved.
Show resolved Hide resolved
return cfg;
}

Expand Down