mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
Add support for run loop queues with no handler, optimize
This commit is contained in:
@@ -464,7 +464,7 @@ static ASDisplayNodeMethodOverrides GetASDisplayNodeMethodOverrides(Class c)
|
||||
static ASRunLoopQueue *queue;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() andHandler:^(id _Nonnull dequeuedItem, BOOL isQueueDrained) { }];
|
||||
queue = [[ASRunLoopQueue alloc] initWithRunLoop:CFRunLoopGetMain() andHandler:nil];
|
||||
queue.batchSize = 10;
|
||||
});
|
||||
|
||||
|
||||
@@ -16,8 +16,19 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ASRunLoopQueue<ObjectType> : NSObject
|
||||
|
||||
/**
|
||||
* Create a new queue with the given run loop and handler.
|
||||
*
|
||||
* @param runloop The run loop that will drive this queue.
|
||||
* @param handlerBlock An optional block to be run for each enqueued object.
|
||||
*
|
||||
* @discussion You may pass @c nil for the handler if you simply want the objects to
|
||||
* be retained at enqueue time, and released during the run loop step. This is useful
|
||||
* for creating a "main deallocation queue", as @c ASDeallocQueue creates its own
|
||||
* worker thread with its own run loop.
|
||||
*/
|
||||
- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop
|
||||
andHandler:(void(^)(ObjectType dequeuedItem, BOOL isQueueDrained))handlerBlock;
|
||||
andHandler:(nullable void(^)(ObjectType dequeuedItem, BOOL isQueueDrained))handlerBlock;
|
||||
|
||||
- (void)enqueue:(ObjectType)object;
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#import <cstdlib>
|
||||
#import <deque>
|
||||
#import <vector>
|
||||
|
||||
#define ASRunLoopQueueLoggingEnabled 0
|
||||
|
||||
@@ -222,7 +223,12 @@ static void runLoopSourceCallback(void *info) {
|
||||
|
||||
- (void)processQueue
|
||||
{
|
||||
std::deque<id> itemsToProcess = std::deque<id>();
|
||||
BOOL hasExecutionBlock = (_queueConsumer != nil);
|
||||
|
||||
// If we have an execution block, this vector will be populated, otherwise remains empty.
|
||||
// This is to avoid needlessly retaining/releasing the objects if we don't have a block.
|
||||
std::vector<id> itemsToProcess;
|
||||
|
||||
BOOL isQueueDrained = NO;
|
||||
{
|
||||
ASDN::MutexLocker l(_internalQueueLock);
|
||||
@@ -235,25 +241,23 @@ static void runLoopSourceCallback(void *info) {
|
||||
ASProfilingSignpostStart(0, self);
|
||||
|
||||
// Snatch the next batch of items.
|
||||
NSUInteger totalNodeCount = _internalQueue.size();
|
||||
for (int i = 0; i < MIN(self.batchSize, totalNodeCount); i++) {
|
||||
id node = _internalQueue[0];
|
||||
itemsToProcess.push_back(node);
|
||||
_internalQueue.pop_front();
|
||||
auto firstItemToProcess = _internalQueue.cbegin();
|
||||
auto lastItemToProcess = MIN(_internalQueue.cend(), firstItemToProcess + self.batchSize);
|
||||
|
||||
if (hasExecutionBlock) {
|
||||
itemsToProcess = std::vector<id>(firstItemToProcess, lastItemToProcess);
|
||||
}
|
||||
_internalQueue.erase(firstItemToProcess, lastItemToProcess);
|
||||
|
||||
if (_internalQueue.empty()) {
|
||||
isQueueDrained = YES;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long numberOfItems = itemsToProcess.size();
|
||||
for (int i = 0; i < numberOfItems; i++) {
|
||||
if (isQueueDrained && i == numberOfItems - 1) {
|
||||
_queueConsumer(itemsToProcess[i], YES);
|
||||
} else {
|
||||
_queueConsumer(itemsToProcess[i], isQueueDrained);
|
||||
}
|
||||
// itemsToProcess will be empty if _queueConsumer == nil so no need to check again.
|
||||
auto itemsEnd = itemsToProcess.cend();
|
||||
for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) {
|
||||
_queueConsumer(*iterator, isQueueDrained && iterator == itemsEnd - 1);
|
||||
}
|
||||
|
||||
// If the queue is not fully drained yet force another run loop to process next batch of items
|
||||
|
||||
Reference in New Issue
Block a user