mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +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 ASRunLoopQueue *queue;
|
||||||
static dispatch_once_t onceToken;
|
static dispatch_once_t onceToken;
|
||||||
dispatch_once(&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;
|
queue.batchSize = 10;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,19 @@ NS_ASSUME_NONNULL_BEGIN
|
|||||||
|
|
||||||
@interface ASRunLoopQueue<ObjectType> : NSObject
|
@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
|
- (instancetype)initWithRunLoop:(CFRunLoopRef)runloop
|
||||||
andHandler:(void(^)(ObjectType dequeuedItem, BOOL isQueueDrained))handlerBlock;
|
andHandler:(nullable void(^)(ObjectType dequeuedItem, BOOL isQueueDrained))handlerBlock;
|
||||||
|
|
||||||
- (void)enqueue:(ObjectType)object;
|
- (void)enqueue:(ObjectType)object;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#import <cstdlib>
|
#import <cstdlib>
|
||||||
#import <deque>
|
#import <deque>
|
||||||
|
#import <vector>
|
||||||
|
|
||||||
#define ASRunLoopQueueLoggingEnabled 0
|
#define ASRunLoopQueueLoggingEnabled 0
|
||||||
|
|
||||||
@@ -222,7 +223,12 @@ static void runLoopSourceCallback(void *info) {
|
|||||||
|
|
||||||
- (void)processQueue
|
- (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;
|
BOOL isQueueDrained = NO;
|
||||||
{
|
{
|
||||||
ASDN::MutexLocker l(_internalQueueLock);
|
ASDN::MutexLocker l(_internalQueueLock);
|
||||||
@@ -235,25 +241,23 @@ static void runLoopSourceCallback(void *info) {
|
|||||||
ASProfilingSignpostStart(0, self);
|
ASProfilingSignpostStart(0, self);
|
||||||
|
|
||||||
// Snatch the next batch of items.
|
// Snatch the next batch of items.
|
||||||
NSUInteger totalNodeCount = _internalQueue.size();
|
auto firstItemToProcess = _internalQueue.cbegin();
|
||||||
for (int i = 0; i < MIN(self.batchSize, totalNodeCount); i++) {
|
auto lastItemToProcess = MIN(_internalQueue.cend(), firstItemToProcess + self.batchSize);
|
||||||
id node = _internalQueue[0];
|
|
||||||
itemsToProcess.push_back(node);
|
if (hasExecutionBlock) {
|
||||||
_internalQueue.pop_front();
|
itemsToProcess = std::vector<id>(firstItemToProcess, lastItemToProcess);
|
||||||
}
|
}
|
||||||
|
_internalQueue.erase(firstItemToProcess, lastItemToProcess);
|
||||||
|
|
||||||
if (_internalQueue.empty()) {
|
if (_internalQueue.empty()) {
|
||||||
isQueueDrained = YES;
|
isQueueDrained = YES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned long numberOfItems = itemsToProcess.size();
|
// itemsToProcess will be empty if _queueConsumer == nil so no need to check again.
|
||||||
for (int i = 0; i < numberOfItems; i++) {
|
auto itemsEnd = itemsToProcess.cend();
|
||||||
if (isQueueDrained && i == numberOfItems - 1) {
|
for (auto iterator = itemsToProcess.begin(); iterator < itemsEnd; iterator++) {
|
||||||
_queueConsumer(itemsToProcess[i], YES);
|
_queueConsumer(*iterator, isQueueDrained && iterator == itemsEnd - 1);
|
||||||
} else {
|
|
||||||
_queueConsumer(itemsToProcess[i], isQueueDrained);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the queue is not fully drained yet force another run loop to process next batch of items
|
// 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