Skip to content

Commit

Permalink
Add a Flag to Disable Main Thread Assertions #trivial (TextureGroup#348)
Browse files Browse the repository at this point in the history
* Add a thread-flag for disabling main thread assertions

* Fix the license header
  • Loading branch information
Adlai-Holler authored and bernieperez committed Apr 25, 2018
1 parent 946824c commit 113aed8
Show file tree
Hide file tree
Showing 5 changed files with 92 additions and 3 deletions.
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

0 comments on commit 113aed8

Please sign in to comment.