Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clean up async transaction system a bit #955

Merged
merged 2 commits into from
Jun 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
## master
* Add your own contributions to the next release on the line below this with your name.
- Make `ASPerformMainThreadDeallocation` visible in C. [Adlai Holler](https://github.com/Adlai-Holler)

- Add snapshot test for astextnode2. [Max Wang](https://github.com/wsdwsd0829) [#935](https://github.com/TextureGroup/Texture/pull/935)
- Internal housekeeping on the async transaction (rendering) system. [Adlai Holler](https://github.com/Adlai-Holler)

## 2.7
- Fix pager node for interface coalescing. [Max Wang](https://github.com/wsdwsd0829) [#877](https://github.com/TextureGroup/Texture/pull/877)
Expand Down
70 changes: 1 addition & 69 deletions Source/Details/Transactions/_ASAsyncTransaction.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ NS_ASSUME_NONNULL_BEGIN
typedef void(^asyncdisplaykit_async_transaction_completion_block_t)(_ASAsyncTransaction *completedTransaction, BOOL canceled);
typedef id<NSObject> _Nullable(^asyncdisplaykit_async_transaction_operation_block_t)(void);
typedef void(^asyncdisplaykit_async_transaction_operation_completion_block_t)(id _Nullable value, BOOL canceled);
typedef void(^asyncdisplaykit_async_transaction_complete_async_operation_block_t)(id _Nullable value);
typedef void(^asyncdisplaykit_async_transaction_async_operation_block_t)(asyncdisplaykit_async_transaction_complete_async_operation_block_t completeOperationBlock);

/**
State is initially ASAsyncTransactionStateOpen.
Expand Down Expand Up @@ -76,30 +74,14 @@ extern NSInteger const ASDefaultTransactionPriority;
/**
A block that is called when the transaction is completed.
*/
@property (nullable, nonatomic, readonly) asyncdisplaykit_async_transaction_completion_block_t completionBlock;
@property (nullable, readonly) asyncdisplaykit_async_transaction_completion_block_t completionBlock;

/**
The state of the transaction.
@see ASAsyncTransactionState
*/
@property (readonly) ASAsyncTransactionState state;

/**
@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 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
queue:(dispatch_queue_t)queue
completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;

/**
@summary Adds a synchronous operation to the transaction. The execution block will be executed immediately.

Expand All @@ -118,56 +100,6 @@ extern NSInteger const ASDefaultTransactionPriority;
queue:(dispatch_queue_t)queue
completion:(nullable 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 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
queue:(dispatch_queue_t)queue
completion:(nullable 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:(NSInteger)priority
queue:(dispatch_queue_t)queue
completion:(nullable asyncdisplaykit_async_transaction_operation_completion_block_t)completion;



/**
@summary Adds a block to run on the completion of the async transaction.

@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)addCompletionBlock:(asyncdisplaykit_async_transaction_completion_block_t)completion;

/**
@summary Cancels all operations in the transaction.

Expand Down
77 changes: 9 additions & 68 deletions Source/Details/Transactions/_ASAsyncTransaction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@
#warning "Texture must be compiled with std=c++11 to prevent layout issues. gnu++ is not supported. This is hopefully temporary."
#endif

#define ASAsyncTransactionAssertMainThread() NSAssert(0 != pthread_main_np(), @"This method must be called on the main thread");

NSInteger const ASDefaultTransactionPriority = 0;

@interface ASAsyncTransactionOperation : NSObject
Expand Down Expand Up @@ -237,7 +235,7 @@ - (NSString *)description
#else
NSUInteger maxThreads = [NSProcessInfo processInfo].activeProcessorCount * 2;

// Bit questionable maybe - we can give main thread more CPU time during tracking;
// Bit questionable maybe - we can give main thread more CPU time during tracking.
if ([[NSRunLoop mainRunLoop].currentMode isEqualToString:UITrackingRunLoopMode])
--maxThreads;
#endif
Expand Down Expand Up @@ -340,8 +338,7 @@ @implementation _ASAsyncTransaction
NSMutableArray<ASAsyncTransactionOperation *> *_operations;
}

#pragma mark -
#pragma mark Lifecycle
#pragma mark - Lifecycle

- (instancetype)initWithCompletionBlock:(void(^)(_ASAsyncTransaction *, BOOL))completionBlock
{
Expand All @@ -363,57 +360,12 @@ - (void)dealloc

#pragma mark - Transaction Management

- (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:(NSInteger)priority
queue:(dispatch_queue_t)queue
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
{
ASAsyncTransactionAssertMainThread();
NSAssert(self.state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions");

[self _ensureTransactionData];

ASAsyncTransactionOperation *operation = [[ASAsyncTransactionOperation alloc] initWithOperationCompletionBlock:completion];
[_operations addObject:operation];
_group->schedule(priority, queue, ^{
@autoreleasepool {
if (self.state != ASAsyncTransactionStateCanceled) {
_group->enter();
block(^(id value){
operation.value = value;
_group->leave();
});
}
}
});
}

- (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:(NSInteger)priority
queue:(dispatch_queue_t)queue
completion:(asyncdisplaykit_async_transaction_operation_completion_block_t)completion
{
ASAsyncTransactionAssertMainThread();
ASDisplayNodeAssertMainThread();
NSAssert(self.state == ASAsyncTransactionStateOpen, @"You can only add operations to open transactions");

[self _ensureTransactionData];
Expand All @@ -429,26 +381,16 @@ - (void)addOperationWithBlock:(asyncdisplaykit_async_transaction_operation_block
});
}

- (void)addCompletionBlock:(asyncdisplaykit_async_transaction_completion_block_t)completion
{
__weak __typeof__(self) weakSelf = self;
dispatch_queue_t bsQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
[self addOperationWithBlock:^(){return (id)nil;} queue:bsQueue completion:^(id value, BOOL canceled) {
__typeof__(self) strongSelf = weakSelf;
completion(strongSelf, canceled);
}];
}

- (void)cancel
{
ASAsyncTransactionAssertMainThread();
ASDisplayNodeAssertMainThread();
NSAssert(self.state != ASAsyncTransactionStateOpen, @"You can only cancel a committed or already-canceled transaction");
self.state = ASAsyncTransactionStateCanceled;
}

- (void)commit
{
ASAsyncTransactionAssertMainThread();
ASDisplayNodeAssertMainThread();
NSAssert(self.state == ASAsyncTransactionStateOpen, @"You cannot double-commit a transaction");
self.state = ASAsyncTransactionStateCommitted;

Expand All @@ -468,7 +410,7 @@ - (void)commit

- (void)completeTransaction
{
ASAsyncTransactionAssertMainThread();
ASDisplayNodeAssertMainThread();
ASAsyncTransactionState state = self.state;
if (state != ASAsyncTransactionStateComplete) {
BOOL isCanceled = (state == ASAsyncTransactionStateCanceled);
Expand All @@ -489,7 +431,7 @@ - (void)completeTransaction

- (void)waitUntilComplete
{
ASAsyncTransactionAssertMainThread();
ASDisplayNodeAssertMainThread();
if (self.state != ASAsyncTransactionStateComplete) {
if (_group) {
_group->wait();
Expand All @@ -500,7 +442,7 @@ - (void)waitUntilComplete
// This is only necessary when forcing display work to complete before allowing the runloop
// to continue, e.g. in the implementation of -[ASDisplayNode recursivelyEnsureDisplay].
if (self.state == ASAsyncTransactionStateOpen) {
[_ASAsyncTransactionGroup commit];
[_ASAsyncTransactionGroup.mainTransactionGroup commit];
NSAssert(self.state != ASAsyncTransactionStateOpen, @"Transaction should not be open after committing group");
}
// If we needed to commit the group above, -completeTransaction may have already been run.
Expand All @@ -510,8 +452,7 @@ - (void)waitUntilComplete
}
}

#pragma mark -
#pragma mark Helper Methods
#pragma mark - Helper Methods

- (void)_ensureTransactionData
{
Expand Down
12 changes: 10 additions & 2 deletions Source/Details/Transactions/_ASAsyncTransactionContainer.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,15 +64,23 @@ typedef NS_ENUM(NSUInteger, ASAsyncTransactionContainerState) {
/**
@summary Returns the current async transaction for this layer. A new transaction is created if one
did not already exist. This method will always return an open, uncommitted transaction.
@desc asyncdisplaykit_isAsyncTransactionContainer does not need to be YES for this to return a transaction.
@desc asyncdisplaykit_asyncTransactionContainer does not need to be YES for this to return a transaction.
Defaults to nil.
*/
@property (nullable, nonatomic, readonly) _ASAsyncTransaction *asyncdisplaykit_asyncTransaction;

/**
@summary Goes up the superlayer chain until it finds the first layer with asyncdisplaykit_isAsyncTransactionContainer=YES (including the receiver) and returns it.
@summary Goes up the superlayer chain until it finds the first layer with asyncdisplaykit_asyncTransactionContainer=YES (including the receiver) and returns it.
Returns nil if no parent container is found.
*/
@property (nullable, nonatomic, readonly) CALayer *asyncdisplaykit_parentTransactionContainer;

/**
@summary Whether or not this layer should serve as a transaction container.
Defaults to NO.
*/
@property (nonatomic, getter=asyncdisplaykit_isAsyncTransactionContainer, setter = asyncdisplaykit_setAsyncTransactionContainer:) BOOL asyncdisplaykit_asyncTransactionContainer;

@end

@interface UIView (ASAsyncTransactionContainer) <ASAsyncTransactionContainer>
Expand Down
43 changes: 4 additions & 39 deletions Source/Details/Transactions/_ASAsyncTransactionContainer.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,53 +20,18 @@

#import <AsyncDisplayKit/_ASAsyncTransaction.h>
#import <AsyncDisplayKit/_ASAsyncTransactionGroup.h>
#import <objc/runtime.h>

static const char *ASDisplayNodeAssociatedTransactionsKey = "ASAssociatedTransactions";
static const char *ASDisplayNodeAssociatedCurrentTransactionKey = "ASAssociatedCurrentTransaction";

@implementation CALayer (ASAsyncTransactionContainerTransactions)

- (NSHashTable *)asyncdisplaykit_asyncLayerTransactions
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey);
}

- (void)asyncdisplaykit_setAsyncLayerTransactions:(NSHashTable *)transactions
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedTransactionsKey, transactions, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@dynamic asyncdisplaykit_asyncLayerTransactions;

// No-ops in the base class. Mostly exposed for testing.
- (void)asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:(_ASAsyncTransaction *)transaction {}
- (void)asyncdisplaykit_asyncTransactionContainerDidCompleteTransaction:(_ASAsyncTransaction *)transaction {}
@end

static const char *ASAsyncTransactionIsContainerKey = "ASTransactionIsContainer";

@implementation CALayer (ASAsyncTransactionContainer)

- (_ASAsyncTransaction *)asyncdisplaykit_currentAsyncTransaction
{
return objc_getAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey);
}

- (void)asyncdisplaykit_setCurrentAsyncTransaction:(_ASAsyncTransaction *)transaction
{
objc_setAssociatedObject(self, ASDisplayNodeAssociatedCurrentTransactionKey, transaction, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)asyncdisplaykit_isAsyncTransactionContainer
{
CFBooleanRef isContainerBool = (__bridge CFBooleanRef)objc_getAssociatedObject(self, ASAsyncTransactionIsContainerKey);
BOOL isContainer = (isContainerBool == kCFBooleanTrue);
return isContainer;
}

- (void)asyncdisplaykit_setAsyncTransactionContainer:(BOOL)isContainer
{
objc_setAssociatedObject(self, ASAsyncTransactionIsContainerKey, (id)(isContainer ? kCFBooleanTrue : kCFBooleanFalse), OBJC_ASSOCIATION_ASSIGN);
}
@dynamic asyncdisplaykit_currentAsyncTransaction;
@dynamic asyncdisplaykit_asyncTransactionContainer;

- (ASAsyncTransactionContainerState)asyncdisplaykit_asyncTransactionContainerState
{
Expand Down Expand Up @@ -109,7 +74,7 @@ - (_ASAsyncTransaction *)asyncdisplaykit_asyncTransaction
self.asyncdisplaykit_currentAsyncTransaction = transaction;
[self asyncdisplaykit_asyncTransactionContainerWillBeginTransaction:transaction];
}
[[_ASAsyncTransactionGroup mainTransactionGroup] addTransactionContainer:self];
[_ASAsyncTransactionGroup.mainTransactionGroup addTransactionContainer:self];
return transaction;
}

Expand Down
16 changes: 12 additions & 4 deletions Source/Details/Transactions/_ASAsyncTransactionGroup.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,29 @@
//

#import <Foundation/Foundation.h>
#import <AsyncDisplayKit/ASBaseDefines.h>

NS_ASSUME_NONNULL_BEGIN

@class _ASAsyncTransaction;
@protocol ASAsyncTransactionContainer;

/// A group of transaction containers, for which the current transactions are committed together at the end of the next runloop tick.
AS_SUBCLASSING_RESTRICTED
@interface _ASAsyncTransactionGroup : NSObject

/// The main transaction group is scheduled to commit on every tick of the main runloop.
+ (_ASAsyncTransactionGroup *)mainTransactionGroup;
+ (void)commit;
/// Access from the main thread only.
@property (class, nonatomic, readonly) _ASAsyncTransactionGroup *mainTransactionGroup;

- (void)commit;

/// Add a transaction container to be committed.
/// @see ASAsyncTransactionContainer
- (void)addTransactionContainer:(id<ASAsyncTransactionContainer>)container;

/// Use the main group.
- (instancetype)init NS_UNAVAILABLE;
+ (instancetype)new NS_UNAVAILABLE;

@end

NS_ASSUME_NONNULL_END
Loading