mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-13 01:40:12 +00:00
[_ASAsyncTransaction] implement operation priority
Signed-off-by: Matej Knopp <matej.knopp@gmail.com>
This commit is contained in:
parent
75abf07c6f
commit
0a45bd9596
@ -66,6 +66,11 @@ typedef NS_OPTIONS(NSUInteger, ASInterfaceState)
|
||||
ASInterfaceStateInHierarchy = ASInterfaceStateMeasureLayout | ASInterfaceStateFetchData | ASInterfaceStateDisplay | ASInterfaceStateVisible,
|
||||
};
|
||||
|
||||
/**
|
||||
* Default drawing priority for display node
|
||||
*/
|
||||
extern NSUInteger const ASDefaultDrawingPriority;
|
||||
|
||||
/**
|
||||
* An `ASDisplayNode` is an abstraction over `UIView` and `CALayer` that allows you to perform calculations about a view
|
||||
* hierarchy off the main thread, and could do rendering off the main thread as well.
|
||||
@ -475,6 +480,13 @@ NS_ASSUME_NONNULL_BEGIN
|
||||
*/
|
||||
@property (nonatomic, assign) NSTimeInterval placeholderFadeDuration;
|
||||
|
||||
/**
|
||||
* @abstract Determines drawing priority of the node. Nodes with higher priority will be drawn earlier.
|
||||
*
|
||||
* @discussion Defaults to ASDefaultDrawingPriority. There may be multiple drawing threads, and some of them may
|
||||
* decide to perform operations in queued order (regardless of drawingPriority)
|
||||
*/
|
||||
@property (nonatomic, assign) NSUInteger drawingPriority;
|
||||
|
||||
/** @name Hit Testing */
|
||||
|
||||
|
||||
@ -26,6 +26,8 @@
|
||||
#import "ASLayoutSpec.h"
|
||||
#import "ASCellNode.h"
|
||||
|
||||
NSUInteger const ASDefaultDrawingPriority = ASDefaultTransactionPriority;
|
||||
|
||||
@interface ASDisplayNode () <UIGestureRecognizerDelegate>
|
||||
|
||||
/**
|
||||
@ -2236,6 +2238,31 @@ static void _recursivelySetDisplaySuspended(ASDisplayNode *node, CALayer *layer,
|
||||
}
|
||||
}
|
||||
|
||||
static const char *ASDisplayNodeDrawingPriorityKey = "ASDrawingPriority";
|
||||
|
||||
- (void)setDrawingPriority:(NSUInteger)drawingPriority
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (drawingPriority == ASDefaultDrawingPriority) {
|
||||
_flags.hasCustomDrawingPriority = NO;
|
||||
objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, nil, OBJC_ASSOCIATION_ASSIGN);
|
||||
} else {
|
||||
_flags.hasCustomDrawingPriority = YES;
|
||||
objc_setAssociatedObject(self, ASDisplayNodeDrawingPriorityKey, [NSNumber numberWithUnsignedInteger:drawingPriority], OBJC_ASSOCIATION_RETAIN);
|
||||
}
|
||||
}
|
||||
|
||||
-(NSUInteger)drawingPriority
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
ASDN::MutexLocker l(_propertyLock);
|
||||
if (!_flags.hasCustomDrawingPriority)
|
||||
return ASDefaultDrawingPriority;
|
||||
else
|
||||
return [objc_getAssociatedObject(self, ASDisplayNodeDrawingPriorityKey) unsignedIntegerValue];
|
||||
}
|
||||
|
||||
- (BOOL)isInHierarchy
|
||||
{
|
||||
ASDisplayNodeAssertThreadAffinity(self);
|
||||
|
||||
@ -29,6 +29,8 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionState) {
|
||||
ASAsyncTransactionStateComplete
|
||||
};
|
||||
|
||||
extern NSUInteger const ASDefaultTransactionPriority;
|
||||
|
||||
/**
|
||||
@summary ASAsyncTransaction provides lightweight transaction semantics for asynchronous operations.
|
||||
|
||||
@ -94,6 +96,25 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionState) {
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
|
||||
|
||||
/**
|
||||
@summary Adds a synchronous operation to the transaction. The execution block will be executed immediately.
|
||||
|
||||
@desc The block will be executed on the specified queue and is expected to complete synchronously. The async
|
||||
transaction will wait for all operations to execute on their appropriate queues, so the blocks may still be executing
|
||||
async if they are running on a concurrent queue, even though the work for this block is synchronous.
|
||||
|
||||
@param block The execution block that will be executed on a background queue. This is where the expensive work goes.
|
||||
@param priority Execution priority; Tasks with higher priority will be executed sooner
|
||||
@param queue The dispatch queue on which to execute the block.
|
||||
@param completion The completion block that will be executed with the output of the execution block when all of the
|
||||
operations in the transaction are completed. Executed and released on callbackQueue.
|
||||
*/
|
||||
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
|
||||
priority:(NSUInteger)priority
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
|
||||
|
||||
|
||||
/**
|
||||
@summary Adds an async operation to the transaction. The execution block will be executed immediately.
|
||||
|
||||
@ -112,6 +133,27 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionState) {
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
|
||||
|
||||
/**
|
||||
@summary Adds an async operation to the transaction. The execution block will be executed immediately.
|
||||
|
||||
@desc The block will be executed on the specified queue and is expected to complete asynchronously. The block will be
|
||||
supplied with a completion block that can be executed once its async operation is completed. This is useful for
|
||||
network downloads and other operations that have an async API.
|
||||
|
||||
WARNING: Consumers MUST call the completeOperationBlock passed into the work block, or objects will be leaked!
|
||||
|
||||
@param block The execution block that will be executed on a background queue. This is where the expensive work goes.
|
||||
@param priority Execution priority; Tasks with higher priority will be executed sooner
|
||||
@param queue The dispatch queue on which to execute the block.
|
||||
@param completion The completion block that will be executed with the output of the execution block when all of the
|
||||
operations in the transaction are completed. Executed and released on callbackQueue.
|
||||
*/
|
||||
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
|
||||
priority:(NSUInteger)priority
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
@summary Adds a block to run on the completion of the async transaction.
|
||||
|
||||
@ -13,6 +13,8 @@
|
||||
#import <list>
|
||||
#import <map>
|
||||
|
||||
NSUInteger const ASDefaultTransactionPriority = 128;
|
||||
|
||||
@interface ASDisplayNodeAsyncTransactionOperation : NSObject
|
||||
- (id)initWithOperationCompletionBlock:(asyncdisplaykit_async_transaction_operation_completion_block_t)operationCompletionBlock;
|
||||
@property (nonatomic, copy) asyncdisplaykit_async_transaction_operation_completion_block_t operationCompletionBlock;
|
||||
@ -63,7 +65,7 @@ public:
|
||||
virtual void release() = 0;
|
||||
|
||||
// schedule block on given queue
|
||||
virtual void schedule(dispatch_queue_t queue, dispatch_block_t block) = 0;
|
||||
virtual void schedule(NSUInteger priority, dispatch_queue_t queue, dispatch_block_t block) = 0;
|
||||
|
||||
// dispatch block on given queue when all previously scheduled blocks finished executing
|
||||
virtual void notify(dispatch_queue_t queue, dispatch_block_t block) = 0;
|
||||
@ -103,7 +105,7 @@ private:
|
||||
}
|
||||
|
||||
virtual void release();
|
||||
virtual void schedule(dispatch_queue_t queue, dispatch_block_t block);
|
||||
virtual void schedule(NSUInteger priority, dispatch_queue_t queue, dispatch_block_t block);
|
||||
virtual void notify(dispatch_queue_t queue, dispatch_block_t block);
|
||||
virtual void enter();
|
||||
virtual void leave();
|
||||
@ -120,12 +122,21 @@ private:
|
||||
{
|
||||
dispatch_block_t _block;
|
||||
GroupImpl *_group;
|
||||
NSUInteger _priority;
|
||||
};
|
||||
|
||||
struct DispatchEntry // entry for each
|
||||
|
||||
struct DispatchEntry // entry for each dispatch queue
|
||||
{
|
||||
std::list<Operation> _operations;
|
||||
typedef std::list<Operation> OperationQueue;
|
||||
typedef std::list<OperationQueue::iterator> OperationIteratorList; // each item points to operation queue
|
||||
typedef std::map<NSUInteger, OperationIteratorList> OperationPriorityMap; // sorted by priority
|
||||
|
||||
OperationQueue _operationQueue;
|
||||
OperationPriorityMap _operationPriorityMap;
|
||||
int _threadCount;
|
||||
|
||||
Operation popNextOperation(bool respectPriority); // assumes locked mutex
|
||||
void pushOperation(Operation operation); // assumes locked mutex
|
||||
};
|
||||
|
||||
std::map<dispatch_queue_t, DispatchEntry> _entries;
|
||||
@ -149,7 +160,44 @@ void ASAsyncTransactionQueue::GroupImpl::release()
|
||||
}
|
||||
}
|
||||
|
||||
void ASAsyncTransactionQueue::GroupImpl::schedule(dispatch_queue_t queue, dispatch_block_t block)
|
||||
ASAsyncTransactionQueue::Operation ASAsyncTransactionQueue::DispatchEntry::popNextOperation(bool respectPriority)
|
||||
{
|
||||
NSCAssert(!_operationQueue.empty() && !_operationPriorityMap.empty(), @"No scheduled operations available");
|
||||
|
||||
OperationQueue::iterator queueIterator;
|
||||
OperationPriorityMap::iterator mapIterator;
|
||||
|
||||
if (respectPriority) {
|
||||
mapIterator = --_operationPriorityMap.end(); // highest priority "bucket"
|
||||
queueIterator = *mapIterator->second.begin();
|
||||
} else {
|
||||
queueIterator = _operationQueue.begin();
|
||||
mapIterator = _operationPriorityMap.find(queueIterator->_priority);
|
||||
}
|
||||
|
||||
// no matter what, first item in "bucket" must match item in queue
|
||||
NSCAssert(mapIterator->second.front() == queueIterator, @"Queue inconsistency");
|
||||
|
||||
Operation res = *queueIterator;
|
||||
_operationQueue.erase(queueIterator);
|
||||
|
||||
mapIterator->second.pop_front();
|
||||
if (mapIterator->second.empty()) {
|
||||
_operationPriorityMap.erase(mapIterator);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void ASAsyncTransactionQueue::DispatchEntry::pushOperation(ASAsyncTransactionQueue::Operation operation)
|
||||
{
|
||||
_operationQueue.push_back(operation);
|
||||
|
||||
OperationIteratorList &list = _operationPriorityMap[operation._priority];
|
||||
list.push_back(--_operationQueue.end());
|
||||
}
|
||||
|
||||
void ASAsyncTransactionQueue::GroupImpl::schedule(NSUInteger priority, dispatch_queue_t queue, dispatch_block_t block)
|
||||
{
|
||||
ASAsyncTransactionQueue &q = _queue;
|
||||
ASDN::MutexLocker locker(q._mutex);
|
||||
@ -159,7 +207,8 @@ void ASAsyncTransactionQueue::GroupImpl::schedule(dispatch_queue_t queue, dispat
|
||||
Operation operation;
|
||||
operation._block = block;
|
||||
operation._group = this;
|
||||
entry._operations.push_back(operation);
|
||||
operation._priority = priority;
|
||||
entry.pushOperation(operation);
|
||||
|
||||
++_pendingOperations; // enter group
|
||||
|
||||
@ -171,15 +220,16 @@ void ASAsyncTransactionQueue::GroupImpl::schedule(dispatch_queue_t queue, dispat
|
||||
|
||||
if (entry._threadCount < maxThreads) { // we need to spawn another thread
|
||||
|
||||
// first thread will take operations in queue order (regardless of priority), other threads will respect priority
|
||||
bool respectPriority = entry._threadCount > 0;
|
||||
++entry._threadCount;
|
||||
|
||||
dispatch_async(queue, ^{
|
||||
ASDN::MutexLocker lock(q._mutex);
|
||||
|
||||
// go until there are no more pending operations
|
||||
while (!entry._operations.empty()) {
|
||||
Operation operation = entry._operations.front();
|
||||
entry._operations.pop_front();
|
||||
while (!entry._operationQueue.empty()) {
|
||||
Operation operation = entry.popNextOperation(respectPriority);
|
||||
{
|
||||
ASDN::MutexUnlocker unlock(q._mutex);
|
||||
if (operation._block) {
|
||||
@ -192,7 +242,7 @@ void ASAsyncTransactionQueue::GroupImpl::schedule(dispatch_queue_t queue, dispat
|
||||
--entry._threadCount;
|
||||
|
||||
if (entry._threadCount == 0) {
|
||||
NSCAssert(entry._operations.empty(), @"No working threads but operations are still scheduled"); // this shouldn't happen
|
||||
NSCAssert(entry._operationQueue.empty() || entry._operationPriorityMap.empty(), @"No working threads but operations are still scheduled"); // this shouldn't happen
|
||||
q._entries.erase(queue);
|
||||
}
|
||||
});
|
||||
@ -295,6 +345,17 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
|
||||
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
|
||||
{
|
||||
[self addAsyncOperationWithBlock:block
|
||||
priority:ASDefaultTransactionPriority
|
||||
queue:queue
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)addAsyncOperationWithBlock:(asyncdisplaykit_async_transaction_async_operation_block_t)block
|
||||
priority:(NSUInteger)priority
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(_state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions");
|
||||
@ -303,7 +364,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
|
||||
|
||||
ASDisplayNodeAsyncTransactionOperation *operation = [[ASDisplayNodeAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
|
||||
[_operations addObject:operation];
|
||||
_group->schedule(queue, ^{
|
||||
_group->schedule(priority, queue, ^{
|
||||
@autoreleasepool {
|
||||
if (_state != ASAsyncTransactionStateCanceled) {
|
||||
_group->enter();
|
||||
@ -319,6 +380,17 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
|
||||
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
|
||||
{
|
||||
[self addOperationWithBlock:block
|
||||
priority:ASDefaultTransactionPriority
|
||||
queue:queue
|
||||
completion:completion];
|
||||
}
|
||||
|
||||
- (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block_t)block
|
||||
priority:(NSUInteger)priority
|
||||
queue:(dispatch_queue_t)queue
|
||||
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
|
||||
{
|
||||
ASDisplayNodeAssertMainThread();
|
||||
ASDisplayNodeAssert(_state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions");
|
||||
@ -327,7 +399,7 @@ ASAsyncTransactionQueue & ASAsyncTransactionQueue::instance()
|
||||
|
||||
ASDisplayNodeAsyncTransactionOperation *operation = [[ASDisplayNodeAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
|
||||
[_operations addObject:operation];
|
||||
_group->schedule(queue, ^{
|
||||
_group->schedule(priority, queue, ^{
|
||||
@autoreleasepool {
|
||||
if (_state != ASAsyncTransactionStateCanceled) {
|
||||
operation.value = block();
|
||||
|
||||
@ -358,7 +358,7 @@ static void __ASDisplayLayerDecrementConcurrentDisplayCount(BOOL displayIsAsync,
|
||||
|
||||
// Adding this displayBlock operation to the transaction will start it IMMEDIATELY.
|
||||
// The only function of the transaction commit is to gate the calling of the completionBlock.
|
||||
[transaction addOperationWithBlock:displayBlock queue:[_ASDisplayLayer displayQueue] completion:completionBlock];
|
||||
[transaction addOperationWithBlock:displayBlock priority:self.drawingPriority queue:[_ASDisplayLayer displayQueue] completion:completionBlock];
|
||||
} else {
|
||||
UIImage *contents = (UIImage *)displayBlock();
|
||||
completionBlock(contents, NO);
|
||||
|
||||
@ -84,6 +84,7 @@ typedef NS_OPTIONS(NSUInteger, ASDisplayNodeMethodOverrides)
|
||||
unsigned shouldRasterizeDescendants:1;
|
||||
unsigned shouldBypassEnsureDisplay:1;
|
||||
unsigned displaySuspended:1;
|
||||
unsigned hasCustomDrawingPriority:1;
|
||||
|
||||
// whether custom drawing is enabled
|
||||
unsigned implementsDrawRect:1;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user