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

Add a Flag to Disable Main Thread Assertions #trivial #348

Merged
merged 3 commits into from
Jun 11, 2017
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
4 changes: 4 additions & 0 deletions AsyncDisplayKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@
CCA282CD1E9EB73E0037E8B7 /* ASTipNode.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282CB1E9EB73E0037E8B7 /* ASTipNode.m */; };
CCA282D01E9EBF6C0037E8B7 /* ASTipsWindow.h in Headers */ = {isa = PBXBuildFile; fileRef = CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */; };
CCA282D11E9EBF6C0037E8B7 /* ASTipsWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */; };
CCA5F62E1EECC2A80060C137 /* ASAssert.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA5F62D1EECC2A80060C137 /* ASAssert.m */; };
CCA5F62C1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = CCA5F62B1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.m */; };
CCB2F34D1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = CCB2F34C1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m */; };
CCBBBF5D1EB161760069AA91 /* ASRangeManagingNode.h in Headers */ = {isa = PBXBuildFile; fileRef = CCBBBF5C1EB161760069AA91 /* ASRangeManagingNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -826,6 +827,7 @@
CCA282CB1E9EB73E0037E8B7 /* ASTipNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTipNode.m; sourceTree = "<group>"; };
CCA282CE1E9EBF6C0037E8B7 /* ASTipsWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASTipsWindow.h; sourceTree = "<group>"; };
CCA282CF1E9EBF6C0037E8B7 /* ASTipsWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASTipsWindow.m; sourceTree = "<group>"; };
CCA5F62D1EECC2A80060C137 /* ASAssert.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASAssert.m; sourceTree = "<group>"; };
CCA5F62A1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSInvocation+ASTestHelpers.h"; sourceTree = "<group>"; };
CCA5F62B1EEC9E9B0060C137 /* NSInvocation+ASTestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSInvocation+ASTestHelpers.m"; sourceTree = "<group>"; };
CCB2F34C1D63CCC6004E6DE9 /* ASDisplayNodeSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ASDisplayNodeSnapshotTests.m; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1368,6 +1370,7 @@
isa = PBXGroup;
children = (
058D0A43195D058D00B7D73C /* ASAssert.h */,
CCA5F62D1EECC2A80060C137 /* ASAssert.m */,
0516FA3A1A15563400B4EBED /* ASAvailability.h */,
058D0A44195D058D00B7D73C /* ASBaseDefines.h */,
1950C4481A3BB5C1005C8279 /* ASEqualityHelpers.h */,
Expand Down Expand Up @@ -2088,6 +2091,7 @@
buildActionMask = 2147483647;
files = (
DEB8ED7C1DD003D300DBDE55 /* ASLayoutTransition.mm in Sources */,
CCA5F62E1EECC2A80060C137 /* ASAssert.m in Sources */,
9F98C0261DBE29E000476D92 /* ASControlTargetAction.m in Sources */,
9C70F2091CDABA36007D6C76 /* ASViewController.mm in Sources */,
3917EBD51E9C2FC400D04A01 /* _ASCollectionReusableView.m in Sources */,
Expand Down
5 changes: 4 additions & 1 deletion Source/ASDisplayNode.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3224,7 +3224,10 @@ - (NSString *)description

- (NSString *)debugDescription
{
return ASObjectDescriptionMake(self, [self propertiesForDebugDescription]);
ASPushMainThreadAssertionsDisabled();
auto result = ASObjectDescriptionMake(self, [self propertiesForDebugDescription]);
ASPopMainThreadAssertionsDisabled();
return result;
}

// This should only be called for debugging. It's not thread safe and it doesn't assert.
Expand Down
22 changes: 20 additions & 2 deletions Source/Base/ASAssert.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

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

#define ASDISPLAYNODE_ASSERTIONS_ENABLED (!defined(NS_BLOCK_ASSERTIONS))

Expand All @@ -42,8 +43,8 @@
#define ASDisplayNodeAssertNotInstantiable() ASDisplayNodeAssert(NO, nil, @"This class is not instantiable.");
#define ASDisplayNodeAssertNotSupported() ASDisplayNodeAssert(NO, nil, @"This method is not supported by class %@", [self class]);

#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(0 != pthread_main_np(), @"This method must be called on the main thread")
#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(0 != pthread_main_np(), @"This function must be called on the main thread")
#define ASDisplayNodeAssertMainThread() ASDisplayNodeAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This method must be called on the main thread")
#define ASDisplayNodeCAssertMainThread() ASDisplayNodeCAssert(ASMainThreadAssertionsAreDisabled() || 0 != pthread_main_np(), @"This function must be called on the main thread")

#define ASDisplayNodeAssertNotMainThread() ASDisplayNodeAssert(0 == pthread_main_np(), @"This method must be called off the main thread")
#define ASDisplayNodeCAssertNotMainThread() ASDisplayNodeCAssert(0 == pthread_main_np(), @"This function must be called off the main thread")
Expand All @@ -69,6 +70,23 @@
#define ASDisplayNodeErrorDomain @"ASDisplayNodeErrorDomain"
#define ASDisplayNodeNonFatalErrorCode 1

/**
* In debug methods, it can be useful to disable main thread assertions to get valuable information,
* even if it means violating threading requirements. These functions are used in -debugDescription and let
* threads decide to suppress/re-enable main thread assertions.
*/
#pragma mark - Main Thread Assertions Disabling

ASDISPLAYNODE_EXTERN_C_BEGIN
BOOL ASMainThreadAssertionsAreDisabled();

void ASPushMainThreadAssertionsDisabled();

void ASPopMainThreadAssertionsDisabled();
ASDISPLAYNODE_EXTERN_C_END

#pragma mark - Non-Fatal Assertions

/// Returns YES if assertion passed, NO otherwise.
#define ASDisplayNodeAssertNonFatal(condition, desc, ...) ({ \
BOOL __evaluated = condition; \
Expand Down
45 changes: 45 additions & 0 deletions Source/Base/ASAssert.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//
// ASAssert.m
// Texture
//
// Copyright (c) 2017-present, Pinterest, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//

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

// pthread_key_create must be called before the key can be used. This function does that.
static pthread_key_t ASMainThreadAssertionsDisabledKey()
{
static pthread_key_t ASMainThreadAssertionsDisabledKey;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
pthread_key_create(&ASMainThreadAssertionsDisabledKey, NULL);
});
return ASMainThreadAssertionsDisabledKey;
}

BOOL ASMainThreadAssertionsAreDisabled() {
return (size_t)pthread_getspecific(ASMainThreadAssertionsDisabledKey()) > 0;
}

void ASPushMainThreadAssertionsDisabled() {
pthread_key_t key = ASMainThreadAssertionsDisabledKey();
size_t oldValue = (size_t)pthread_getspecific(key);
pthread_setspecific(key, (void *)(oldValue + 1));
}

void ASPopMainThreadAssertionsDisabled() {
pthread_key_t key = ASMainThreadAssertionsDisabledKey();
size_t oldValue = (size_t)pthread_getspecific(key);
if (oldValue > 0) {
pthread_setspecific(key, (void *)(oldValue - 1));
} else {
ASDisplayNodeCFailAssert(@"Attempt to pop thread assertion-disabling without corresponding push.");
}
}
19 changes: 19 additions & 0 deletions Tests/ASDisplayNodeTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2260,4 +2260,23 @@ - (void)testThatConvertPointGoesToWindowWhenPassedNil_layerBacked
ASXCTAssertEqualPoints([node convertPoint:node.bounds.origin toNode:nil], expectedOrigin);
}

- (void)testThatItIsAllowedToRetrieveDebugDescriptionIncludingVCOffMainThread
{
ASDisplayNode *node = [[ASDisplayNode alloc] init];
UIViewController *vc = [[UIViewController alloc] init];
[vc.view addSubnode:node];
dispatch_group_t g = dispatch_group_create();
dispatch_group_enter(g);
__block NSString *debugDescription;
[NSThread detachNewThreadWithBlock:^{
debugDescription = [node debugDescription];
dispatch_group_leave(g);
}];
dispatch_group_wait(g, DISPATCH_TIME_FOREVER);
// Ensure the debug description contains the VC string.
// Have to split into two lines because XCTAssert macro can't handle the stringWithFormat:.
BOOL hasVC = [debugDescription containsString:[NSString stringWithFormat:@"%p", vc]];
XCTAssert(hasVC);
}

@end