mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 22:55:00 +00:00
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:
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user