mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2026-01-06 13:12:49 +00:00
Rollout ASDeallocQueueV2 #trivial (#1143)
This commit is contained in:
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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<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.");
|
||||
|
||||
@@ -47,14 +47,3 @@
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation ASConfiguration (UserProvided)
|
||||
|
||||
+ (ASConfiguration *)textureConfiguration
|
||||
{
|
||||
ASConfiguration *cfg = [[ASConfiguration alloc] init];
|
||||
cfg.experimentalFeatures = ASExperimentalDeallocQueue;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
Reference in New Issue
Block a user