Skip to content

Commit

Permalink
Change experiment gating and add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Adlai-Holler committed Jul 1, 2019
1 parent 691ddde commit 6e9fb4a
Show file tree
Hide file tree
Showing 19 changed files with 256 additions and 61 deletions.
4 changes: 4 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@
CC3B208C1C3F7A5400798563 /* ASWeakSet.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B20881C3F7A5400798563 /* ASWeakSet.mm */; };
CC3B208E1C3F7D0A00798563 /* ASWeakSetTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.mm */; };
CC3B20901C3F892D00798563 /* ASBridgedPropertiesTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */; };
CC3F59C022CAACAD002C3824 /* ASNodeContextTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC3F59BF22CAACAD002C3824 /* ASNodeContextTests.mm */; };
CC4981B31D1A02BE004E13CC /* ASTableViewThrashTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.mm */; };
CC54A81C1D70079800296A24 /* ASDispatch.h in Headers */ = {isa = PBXBuildFile; fileRef = CC54A81B1D70077A00296A24 /* ASDispatch.h */; settings = {ATTRIBUTES = (Private, ); }; };
CC54A81E1D7008B300296A24 /* ASDispatchTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = CC54A81D1D7008B300296A24 /* ASDispatchTests.mm */; };
Expand Down Expand Up @@ -890,6 +891,7 @@
CC3B20881C3F7A5400798563 /* ASWeakSet.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASWeakSet.mm; sourceTree = "<group>"; };
CC3B208D1C3F7D0A00798563 /* ASWeakSetTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASWeakSetTests.mm; sourceTree = "<group>"; };
CC3B208F1C3F892D00798563 /* ASBridgedPropertiesTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; path = ASBridgedPropertiesTests.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
CC3F59BF22CAACAD002C3824 /* ASNodeContextTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ASNodeContextTests.mm; sourceTree = "<group>"; };
CC4981B21D1A02BE004E13CC /* ASTableViewThrashTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASTableViewThrashTests.mm; sourceTree = "<group>"; };
CC4981BA1D1C7F65004E13CC /* NSIndexSet+ASHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSIndexSet+ASHelpers.h"; sourceTree = "<group>"; };
CC4981BB1D1C7F65004E13CC /* NSIndexSet+ASHelpers.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "NSIndexSet+ASHelpers.mm"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1353,6 +1355,7 @@
058D0A32195D057000B7D73C /* ASMutableAttributedStringBuilderTests.mm */,
BB5FC3CD1F9BA688007F191E /* ASNavigationControllerTests.mm */,
CC11F9791DB181180024D77B /* ASNetworkImageNodeTests.mm */,
CC3F59BF22CAACAD002C3824 /* ASNodeContextTests.mm */,
ACF6ED591B178DC700DA7C62 /* ASOverlayLayoutSpecSnapshotTests.mm */,
AE6987C01DD04E1000B9E458 /* ASPagerNodeTests.mm */,
CC8B05D41D73836400F54286 /* ASPerformanceTestContext.h */,
Expand Down Expand Up @@ -2353,6 +2356,7 @@
254C6B541BF8FF2A003EC431 /* ASTextKitTests.mm in Sources */,
05EA6FE71AC0966E00E35788 /* ASSnapshotTestCase.mm in Sources */,
CC35CEC620DD87280006448D /* ASCollectionsTests.mm in Sources */,
CC3F59C022CAACAD002C3824 /* ASNodeContextTests.mm in Sources */,
ACF6ED631B178DC700DA7C62 /* ASStackLayoutSpecSnapshotTests.mm in Sources */,
CCE4F9BA1F0DBB5000062E4E /* ASLayoutTestNode.mm in Sources */,
CCAA0B82206ADECB0057B336 /* ASRecursiveUnfairLockTests.mm in Sources */,
Expand Down
4 changes: 0 additions & 4 deletions Source/ASCollectionView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1953,19 +1953,15 @@ - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAt
}
if (!block && !cell && _asyncDataSourceFlags.collectionNodeNodeForItem) {
GET_COLLECTIONNODE_OR_RETURN(collectionNode, ^{ return [[ASCellNode alloc] init]; });
ASNodeContextPushNew();
cell = [_asyncDataSource collectionNode:collectionNode nodeForItemAtIndexPath:indexPath];
ASNodeContextPop();
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
if (!block && !cell && _asyncDataSourceFlags.collectionViewNodeBlockForItem) {
block = [_asyncDataSource collectionView:self nodeBlockForItemAtIndexPath:indexPath];
}
if (!block && !cell && _asyncDataSourceFlags.collectionViewNodeForItem) {
ASNodeContextPushNew();
cell = [_asyncDataSource collectionView:self nodeForItemAtIndexPath:indexPath];
ASNodeContextPop();
}
#pragma clang diagnostic pop

Expand Down
1 change: 1 addition & 0 deletions Source/ASDisplayNode+Yoga.mm
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ - (void)_locked_insertYogaChild:(ASDisplayNode *)child atIndex:(NSUInteger)index
if (child == nil) {
return;
}
ASDisplayNodeAssert(_nodeContext == child->_nodeContext, @"Cannot add yoga child from different node context.");
if (_yogaChildren == nil) {
_yogaChildren = [[NSMutableArray alloc] init];
}
Expand Down
1 change: 0 additions & 1 deletion Source/ASDisplayNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ AS_EXTERN NSInteger const ASDefaultDrawingPriority;
*/
- (instancetype)init NS_DESIGNATED_INITIALIZER;


/**
* @abstract Alternative initializer with a block to create the backing view.
*
Expand Down
9 changes: 3 additions & 6 deletions Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -302,13 +302,10 @@ - (void)_staticInitialize
- (void)_initializeInstance
{
[self _staticInitialize];
if (ASNodeContext *ctx = ASNodeContextGet()) {
_nodeContext = ctx;
new (&_mutexOrPtr) AS::MutexOrPointer(&ctx->_mutex);
_nodeContext = ASNodeContextGet();
if (_nodeContext) {
new (&_mutexOrPtr) AS::MutexOrPointer(&_nodeContext->_mutex);
} else {
ASDisplayNodeAssert(!ASActivateExperimentalFeature(ASExperimentalNodeContext), @"In node context experiment, but no"
"node context. Did you forget to push one before creating your node for an ASViewController?"
"Please report to the maintainers what circumstance this happened in if it seems unusual.");
new (&_mutexOrPtr) AS::MutexOrPointer(nullptr);
_mutexOrPtr.get().SetDebugNameWithObject(self);
}
Expand Down
1 change: 0 additions & 1 deletion Source/ASExperimentalFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ typedef NS_OPTIONS(NSUInteger, ASExperimentalFeatures) {
ASExperimentalRemoveTextKitInitialisingLock = 1 << 10, // exp_remove_textkit_initialising_lock
ASExperimentalDrawingGlobal = 1 << 11, // exp_drawing_global
ASExperimentalOptimizeDataControllerPipeline = 1 << 12, // exp_optimize_data_controller_pipeline
ASExperimentalNodeContext = 1 << 13, // exp_node_context
ASExperimentalFeatureAll = 0xFFFFFFFF
};

Expand Down
3 changes: 1 addition & 2 deletions Source/ASExperimentalFeatures.mm
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
@"exp_oom_bg_dealloc_disable",
@"exp_remove_textkit_initialising_lock",
@"exp_drawing_global",
@"exp_optimize_data_controller_pipeline",
@"exp_node_context"]));
@"exp_optimize_data_controller_pipeline"]));
if (flags == ASExperimentalFeatureAll) {
return allNames;
}
Expand Down
40 changes: 22 additions & 18 deletions Source/ASNodeContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,14 @@ NS_ASSUME_NONNULL_BEGIN
@class ASNodeContext;

/**
* Push the given context, which will apply to any nodes initialized with `init` instead of `initWithContext:`.
* Push the given context, which will apply to any nodes initialized until the corresponding `pop`.
*
* A default context is provided by the system under the following circumstances:
* - During the execution of a node block for ASCollectionNode or ASTableNode.
* - During the call to `nodeForItem:` & related methods for ASCollectionNode or ASTableNode.
* - During the execution of `calculateLayoutThatFits:` or `layoutSpecThatFits:` (context of receiver is pushed.)
*
* @discussion Generally users will not need to call this function themselves.
* Generally each cell in a collection or table, and the root of an ASViewController, will be a context.
*/
AS_EXTERN void ASNodeContextPush(unowned ASNodeContext *context);

/**
* Creates a new context and pushes it. This is useful during experimentation, so that if you aren't in the context
* experiment, you won't create a context for no reason.
*/
AS_EXTERN void ASNodeContextPushNew(void);

/**
* Get the current default context, if there is one.
* Get the current top context, if there is one.
*/
AS_EXTERN ASNodeContext *_Nullable ASNodeContextGet(void);

Expand All @@ -41,6 +30,18 @@ AS_EXTERN ASNodeContext *_Nullable ASNodeContextGet(void);
*/
AS_EXTERN void ASNodeContextPop(void);

/**
* A convenience to perform the given block with the provided context active.
*/
AS_EXTERN id ASNodeContextPerform(unowned ASNodeContext *ctx, id (^NS_NOESCAPE body)(void));

/**
* Node contexts can have extensions attached to them. For instance, if your application wants to do custom logging
* on a per-context basis, you can define an extension identifier (pick an arbitrary uint32_t that won't collide)
* and the logger can be stored & retrieved from the node context.
*/
typedef uint32_t ASNodeContextExtensionIdentifier NS_TYPED_EXTENSIBLE_ENUM;

/**
* A node context is an object that is shared by, and uniquely identifies, an "embedding" of nodes. For example,
* each cell in a collection view has its own context. Each ASViewController's node has its own context. You can
Expand All @@ -51,12 +52,15 @@ AS_EXTERN void ASNodeContextPop(void);
*
* Nodes may not be moved from one context to another. For instance, you may not detach a subnode of a cell node,
* and reattach it to a subtree of another cell node in the same or another collection view.
*
* Node contexts are established in the `-initWithContext:` method and do not change. For ease of use, a default
* context will be used from the `-init` method if available.
*/
AS_SUBCLASSING_RESTRICTED
@interface ASNodeContext : NSObject
@interface ASNodeContext : NSObject <ASLocking>

#pragma mark - Extension

- (nullable id)extensionWithIdentifier:(ASNodeContextExtensionIdentifier)extensionIdentifier;

- (void)setExtension:(nullable id)extension forIdentifier:(ASNodeContextExtensionIdentifier)extensionIdentifier;

@end

Expand Down
65 changes: 51 additions & 14 deletions Source/ASNodeContext.mm
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@

#import <AsyncDisplayKit/ASAssert.h>

#if AS_TLS_AVAILABLE

#import <stack>
#import <unordered_map>

#if AS_TLS_AVAILABLE

static thread_local std::stack<ASNodeContext *> gContexts;

void _ASNodeContextPushNoCheck(unowned ASNodeContext *context) {
void ASNodeContextPush(unowned ASNodeContext *context) {
gContexts.push(context);
}

Expand All @@ -24,10 +25,11 @@ void _ASNodeContextPushNoCheck(unowned ASNodeContext *context) {
}

void ASNodeContextPop() {
if (ASActivateExperimentalFeature(ASExperimentalNodeContext)) {
ASDisplayNodeCAssertFalse(gContexts.empty());
gContexts.pop();
if (DISPATCH_EXPECT(gContexts.empty(), false)) {
ASDisplayNodeCFailAssert(@"Attempt to pop empty context stack.");
return;
}
gContexts.pop();
}

#else // !AS_TLS_AVAILABLE
Expand All @@ -37,7 +39,7 @@ void ASNodeContextPop() {
// Points to a NSMutableArray<ASNodeContext *>.
static constexpr NSString *ASNodeContextStackKey = @"org.TextureGroup.Texture.nodeContexts";

void _ASNodeContextPushNoCheck(unowned ASNodeContext *context) {
void ASNodeContextPush(unowned ASNodeContext *context) {
unowned NSMutableDictionary *td = NSThread.currentThread.threadDictionary;
unowned NSMutableArray<ASNodeContext *> *stack = td[ASNodeContextStackKey];
if (!stack) {
Expand All @@ -59,18 +61,53 @@ void ASNodeContextPop() {

#endif // !AS_TLS_AVAILABLE

void ASNodeContextPush(unowned ASNodeContext *context) {
if (ASActivateExperimentalFeature(ASExperimentalNodeContext)) {
_ASNodeContextPushNoCheck(context);
id ASNodeContextPerform(unowned ASNodeContext *ctx, id(^ NS_NOESCAPE body)(void))
{
ASNodeContextPush(ctx);
id result = body();
ASNodeContextPop();
return result;
}

@implementation ASNodeContext {
std::unordered_map<uint32_t, id> _extensions;
}

- (id)createObject:(id(^ NS_NOESCAPE)())body
{
ASNodeContextPush(self);
id result = body();
ASNodeContextPop();
return result;
}

- (instancetype)init
{
if (self = [super init]) {
_mutex.SetDebugNameWithObject(self);
}
return self;
}

void ASNodeContextPushNew() {
if (ASActivateExperimentalFeature(ASExperimentalNodeContext)) {
_ASNodeContextPushNoCheck([[ASNodeContext alloc] init]);
- (id)extensionWithIdentifier:(ASNodeContextExtensionIdentifier)extensionIdentifier
{
AS::MutexLocker l(_mutex);
auto it = _extensions.find(extensionIdentifier);
return it != _extensions.end() ? it->second : nil;
}

- (void)setExtension:(id)extension forIdentifier:(ASNodeContextExtensionIdentifier)extensionIdentifier
{
AS::MutexLocker l(_mutex);
if (extension) {
_extensions.emplace(extensionIdentifier, extension);
} else {
_extensions.erase(extensionIdentifier);
}
}

@implementation ASNodeContext
#pragma mark ASLocking

ASSynthesizeLockingMethodsWithMutex(_mutex);

@end
6 changes: 3 additions & 3 deletions Source/ASNodeController+Beta.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ @implementation ASNodeController
- (instancetype)init
{
if (self = [super init]) {
if (ASNodeContext *ctx = ASNodeContextGet()) {
_nodeContext = ctx;
new (&_mutexOrPtr) AS::MutexOrPointer(&ctx->_mutex);
_nodeContext = ASNodeContextGet();
if (_nodeContext) {
new (&_mutexOrPtr) AS::MutexOrPointer(&_nodeContext->_mutex);
} else {
new (&_mutexOrPtr) AS::MutexOrPointer(nullptr);
_mutexOrPtr.get().SetDebugNameWithObject(self);
Expand Down
4 changes: 0 additions & 4 deletions Source/ASTableView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1744,9 +1744,7 @@ - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAt
} else if (_asyncDataSourceFlags.tableNodeNodeForRow) {
ASCellNode *node = nil;
if (ASTableNode *tableNode = self.tableNode) {
ASNodeContextPushNew();
node = [_asyncDataSource tableNode:tableNode nodeForRowAtIndexPath:indexPath];
ASNodeContextPop();
}
if ([node isKindOfClass:[ASCellNode class]]) {
block = ^{
Expand All @@ -1760,9 +1758,7 @@ - (ASCellNodeBlock)dataController:(ASDataController *)dataController nodeBlockAt
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
block = [_asyncDataSource tableView:self nodeBlockForRowAtIndexPath:indexPath];
} else if (_asyncDataSourceFlags.tableViewNodeForRow) {
ASNodeContextPushNew();
ASCellNode *node = [_asyncDataSource tableView:self nodeForRowAtIndexPath:indexPath];
ASNodeContextPop();
#pragma clang diagnostic pop
if ([node isKindOfClass:[ASCellNode class]]) {
block = ^{
Expand Down
2 changes: 0 additions & 2 deletions Source/Details/ASDataController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -166,13 +166,11 @@ - (void)_allocateNodesFromElements:(NSArray<ASCollectionElement *> *)elements

unowned ASCollectionElement *element = elements[i];

ASNodeContextPushNew();
NSMutableDictionary *dict = [[NSThread currentThread] threadDictionary];
dict[ASThreadDictMaxConstraintSizeKey] =
[NSValue valueWithCGSize:element.constrainedSize.max];
unowned ASCellNode *node = element.node;
[dict removeObjectForKey:ASThreadDictMaxConstraintSizeKey];
ASNodeContextPop();

// Layout the node if the size range is valid.
ASSizeRange sizeRange = element.constrainedSize;
Expand Down
4 changes: 1 addition & 3 deletions Tests/ASConfigurationTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,7 @@
ASExperimentalOOMBackgroundDeallocDisable,
ASExperimentalRemoveTextKitInitialisingLock,
ASExperimentalDrawingGlobal,
ASExperimentalOptimizeDataControllerPipeline,
ASExperimentalNodeContext
ASExperimentalOptimizeDataControllerPipeline
};

@interface ASConfigurationTests : ASTestCase <ASConfigurationDelegate>
Expand All @@ -57,7 +56,6 @@ + (NSArray *)names {
@"exp_remove_textkit_initialising_lock",
@"exp_drawing_global",
@"exp_optimize_data_controller_pipeline",
@"exp_node_context"
];
}

Expand Down
Loading

0 comments on commit 6e9fb4a

Please sign in to comment.