Skip to content
This repository was archived by the owner on Feb 2, 2023. It is now read-only.

Commit 745f724

Browse files
committed
Merge pull request #933 from garrettmoon/fixCrashRelatedToOutOfOrderMainQueueBlocks
Fixes an issue where reloadDataImmediately interleaves with asynchronous loading in ASDataController.
2 parents 2d9064d + 941a732 commit 745f724

4 files changed

Lines changed: 112 additions & 15 deletions

File tree

AsyncDisplayKit.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,10 @@
257257
509E68661B3AEDD7009B9150 /* CGRect+ASConvenience.m in Sources */ = {isa = PBXBuildFile; fileRef = 205F0E201B376416007741D0 /* CGRect+ASConvenience.m */; };
258258
68B0277A1C1A79CC0041016B /* ASDisplayNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
259259
68B0277B1C1A79D60041016B /* ASDisplayNode+Beta.h in Headers */ = {isa = PBXBuildFile; fileRef = 68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */; settings = {ATTRIBUTES = (Public, ); }; };
260+
68EE0DBD1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */; };
261+
68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */; };
262+
68EE0DBF1C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */; };
263+
68EE0DC01C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */ = {isa = PBXBuildFile; fileRef = 68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */; };
260264
6BDC61F61979037800E50D21 /* AsyncDisplayKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */; settings = {ATTRIBUTES = (Public, ); }; };
261265
92DD2FE31BF4B97E0074C9DD /* ASMapNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */; settings = {ATTRIBUTES = (Public, ); }; };
262266
92DD2FE41BF4B97E0074C9DD /* ASMapNode.mm in Sources */ = {isa = PBXBuildFile; fileRef = 92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */; };
@@ -662,6 +666,8 @@
662666
4640521C1A3F83C40061C0BA /* ASFlowLayoutController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASFlowLayoutController.mm; sourceTree = "<group>"; };
663667
4640521D1A3F83C40061C0BA /* ASLayoutController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASLayoutController.h; sourceTree = "<group>"; };
664668
68B027791C1A79CC0041016B /* ASDisplayNode+Beta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "ASDisplayNode+Beta.h"; sourceTree = "<group>"; };
669+
68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMainSerialQueue.h; sourceTree = "<group>"; };
670+
68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMainSerialQueue.mm; sourceTree = "<group>"; };
665671
6BDC61F51978FEA400E50D21 /* AsyncDisplayKit.h */ = {isa = PBXFileReference; explicitFileType = sourcecode.c.h; path = AsyncDisplayKit.h; sourceTree = "<group>"; };
666672
92DD2FE11BF4B97E0074C9DD /* ASMapNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ASMapNode.h; sourceTree = "<group>"; };
667673
92DD2FE21BF4B97E0074C9DD /* ASMapNode.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ASMapNode.mm; sourceTree = "<group>"; };
@@ -1002,6 +1008,8 @@
10021008
05F20AA31A15733C00DCA68A /* ASImageProtocols.h */,
10031009
4640521D1A3F83C40061C0BA /* ASLayoutController.h */,
10041010
292C59991A956527007E5DD6 /* ASLayoutRangeType.h */,
1011+
68EE0DBB1C1B4ED300BA1B99 /* ASMainSerialQueue.h */,
1012+
68EE0DBC1C1B4ED300BA1B99 /* ASMainSerialQueue.mm */,
10051013
058D09E8195D050800B7D73C /* ASMutableAttributedStringBuilder.h */,
10061014
058D09E9195D050800B7D73C /* ASMutableAttributedStringBuilder.m */,
10071015
055F1A3619ABD413004DAFF1 /* ASRangeController.h */,
@@ -1229,6 +1237,7 @@
12291237
058D0A74195D05F800B7D73C /* _ASPendingState.h in Headers */,
12301238
9C5586691BD549CB00B50E3A /* ASAsciiArtBoxCreator.h in Headers */,
12311239
058D0A76195D05F900B7D73C /* _ASScopeTimer.h in Headers */,
1240+
68EE0DBD1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */,
12321241
257754B31BEE44CD00737CA5 /* ASTextKitTailTruncater.h in Headers */,
12331242
205F0E191B37339C007741D0 /* ASAbstractLayoutController.h in Headers */,
12341243
058D0A82195D060300B7D73C /* ASAssert.h in Headers */,
@@ -1350,6 +1359,7 @@
13501359
B35062491B010EFD0018CF92 /* _ASCoreAnimationExtras.h in Headers */,
13511360
B350620F1B010EFD0018CF92 /* _ASDisplayLayer.h in Headers */,
13521361
B35062111B010EFD0018CF92 /* _ASDisplayView.h in Headers */,
1362+
68EE0DBE1C1B4ED300BA1B99 /* ASMainSerialQueue.h in Headers */,
13531363
B350624B1B010EFD0018CF92 /* _ASPendingState.h in Headers */,
13541364
9C55866C1BD54A3000B50E3A /* ASAsciiArtBoxCreator.h in Headers */,
13551365
B350624D1B010EFD0018CF92 /* _ASScopeTimer.h in Headers */,
@@ -1656,6 +1666,7 @@
16561666
AC026B711BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */,
16571667
257754BF1BEE458E00737CA5 /* ASTextKitCoreTextAdditions.m in Sources */,
16581668
058D0A18195D050800B7D73C /* _ASDisplayLayer.mm in Sources */,
1669+
68EE0DBF1C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */,
16591670
058D0A19195D050800B7D73C /* _ASDisplayView.mm in Sources */,
16601671
9C55866A1BD549CB00B50E3A /* ASAsciiArtBoxCreator.m in Sources */,
16611672
058D0A27195D050800B7D73C /* _ASPendingState.m in Sources */,
@@ -1783,6 +1794,7 @@
17831794
AC026B721BD57DBF00BBC17E /* _ASHierarchyChangeSet.m in Sources */,
17841795
B35062421B010EFD0018CF92 /* _ASAsyncTransactionGroup.m in Sources */,
17851796
B350624A1B010EFD0018CF92 /* _ASCoreAnimationExtras.mm in Sources */,
1797+
68EE0DC01C1B4ED300BA1B99 /* ASMainSerialQueue.mm in Sources */,
17861798
2767E9421BB19BD600EA9B77 /* ASViewController.m in Sources */,
17871799
B35062101B010EFD0018CF92 /* _ASDisplayLayer.mm in Sources */,
17881800
9C55866B1BD54A1900B50E3A /* ASAsciiArtBoxCreator.m in Sources */,

AsyncDisplayKit/Details/ASDataController.mm

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#import "ASAssert.h"
1414
#import "ASCellNode.h"
1515
#import "ASDisplayNode.h"
16+
#import "ASMainSerialQueue.h"
1617
#import "ASMultidimensionalArrayUtils.h"
1718
#import "ASInternalHelpers.h"
1819
#import "ASLayout.h"
@@ -31,7 +32,7 @@ @interface ASDataController () {
3132
NSMutableDictionary *_completedNodes; // Main thread only. External data access can immediately query this if _externalCompletedNodes is unavailable.
3233
NSMutableDictionary *_editingNodes; // Modified on _editingTransactionQueue only. Updates propogated to _completedNodes.
3334

34-
35+
ASMainSerialQueue *_mainSerialQueue;
3536

3637
NSMutableArray *_pendingEditCommandBlocks; // To be run on the main thread. Handles begin/endUpdates tracking.
3738
NSOperationQueue *_editingTransactionQueue; // Serial background queue. Dispatches concurrent layout and manages _editingNodes.
@@ -63,6 +64,8 @@ - (instancetype)initWithAsyncDataFetching:(BOOL)asyncDataFetchingEnabled
6364
_completedNodes[ASDataControllerRowNodeKind] = [NSMutableArray array];
6465
_editingNodes[ASDataControllerRowNodeKind] = [NSMutableArray array];
6566

67+
_mainSerialQueue = [[ASMainSerialQueue alloc] init];
68+
6669
_pendingEditCommandBlocks = [NSMutableArray array];
6770

6871
_editingTransactionQueue = [[NSOperationQueue alloc] init];
@@ -208,12 +211,12 @@ - (void)insertNodes:(NSArray *)nodes ofKind:(NSString *)kind atIndexPaths:(NSArr
208211
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
209212
NSMutableArray *completedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(editingNodes);
210213

211-
ASPerformBlockOnMainThread(^{
214+
[_mainSerialQueue performBlockOnMainThread:^{
212215
_completedNodes[kind] = completedNodes;
213216
if (completionBlock) {
214217
completionBlock(nodes, indexPaths);
215218
}
216-
});
219+
}];
217220
}
218221

219222
- (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths completion:(void (^)(NSArray *nodes, NSArray *indexPaths))completionBlock
@@ -227,13 +230,13 @@ - (void)deleteNodesOfKind:(NSString *)kind atIndexPaths:(NSArray *)indexPaths co
227230
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(editingNodes, indexPaths);
228231
_editingNodes[kind] = editingNodes;
229232

230-
ASPerformBlockOnMainThread(^{
233+
[_mainSerialQueue performBlockOnMainThread:^{
231234
NSArray *nodes = ASFindElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths);
232235
ASDeleteElementsInMultidimensionalArrayAtIndexPaths(_completedNodes[kind], indexPaths);
233236
if (completionBlock) {
234237
completionBlock(nodes, indexPaths);
235238
}
236-
});
239+
}];
237240
}
238241

239242
- (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSArray *sections, NSIndexSet *indexSet))completionBlock
@@ -250,25 +253,25 @@ - (void)insertSections:(NSMutableArray *)sections ofKind:(NSString *)kind atInde
250253
// Deep copy is critical here, or future edits to the sub-arrays will pollute state between _editing and _complete on different threads.
251254
NSArray *sectionsForCompleted = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(sections);
252255

253-
ASPerformBlockOnMainThread(^{
256+
[_mainSerialQueue performBlockOnMainThread:^{
254257
[_completedNodes[kind] insertObjects:sectionsForCompleted atIndexes:indexSet];
255258
if (completionBlock) {
256259
completionBlock(sections, indexSet);
257260
}
258-
});
261+
}];
259262
}
260263

261264
- (void)deleteSectionsOfKind:(NSString *)kind atIndexSet:(NSIndexSet *)indexSet completion:(void (^)(NSIndexSet *indexSet))completionBlock
262265
{
263266
if (indexSet.count == 0)
264267
return;
265268
[_editingNodes[kind] removeObjectsAtIndexes:indexSet];
266-
ASPerformBlockOnMainThread(^{
269+
[_mainSerialQueue performBlockOnMainThread:^{
267270
[_completedNodes[kind] removeObjectsAtIndexes:indexSet];
268271
if (completionBlock) {
269272
completionBlock(indexSet);
270273
}
271-
});
274+
}];
272275
}
273276

274277
#pragma mark - Internal Data Querying + Editing
@@ -512,14 +515,14 @@ - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
512515
LOG(@"endUpdatesWithCompletion - beginning");
513516

514517
[_editingTransactionQueue addOperationWithBlock:^{
515-
ASPerformBlockOnMainThread(^{
518+
[_mainSerialQueue performBlockOnMainThread:^{
516519
// Deep copy _completedNodes to _externalCompletedNodes.
517520
// Any external queries from now on will be done on _externalCompletedNodes, to guarantee data consistency with the delegate.
518521
_externalCompletedNodes = (NSMutableArray *)ASMultidimensionalArrayDeepMutableCopy(_completedNodes[ASDataControllerRowNodeKind]);
519522

520523
LOG(@"endUpdatesWithCompletion - begin updates call to delegate");
521524
[_delegate dataControllerBeginUpdates:self];
522-
});
525+
}];
523526
}];
524527

525528
// Running these commands may result in blocking on an _editingTransactionQueue operation that started even before -beginUpdates.
@@ -532,13 +535,13 @@ - (void)endUpdatesAnimated:(BOOL)animated completion:(void (^)(BOOL))completion
532535
[_pendingEditCommandBlocks removeAllObjects];
533536

534537
[_editingTransactionQueue addOperationWithBlock:^{
535-
ASPerformBlockOnMainThread(^{
538+
[_mainSerialQueue performBlockOnMainThread:^{
536539
// Now that the transaction is done, _completedNodes can be accessed externally again.
537540
_externalCompletedNodes = nil;
538541

539542
LOG(@"endUpdatesWithCompletion - calling delegate end");
540543
[_delegate dataController:self endUpdatesAnimated:animated completion:completion];
541-
});
544+
}];
542545
}];
543546
}
544547
}
@@ -819,11 +822,11 @@ - (void)relayoutAllNodes
819822
// i.e there might be some nodes that were measured using the old constrained size but haven't been added to _completedNodes
820823
// (see _layoutNodes:atIndexPaths:withAnimationOptions:).
821824
[_editingTransactionQueue addOperationWithBlock:^{
822-
ASPerformBlockOnMainThread(^{
825+
[_mainSerialQueue performBlockOnMainThread:^{
823826
for (NSString *kind in [_completedNodes keyEnumerator]) {
824827
[self _relayoutNodesOfKind:kind];
825828
}
826-
});
829+
}];
827830
}];
828831
}];
829832
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// ASMainSerialQueue.h
3+
// AsyncDisplayKit
4+
//
5+
// Created by Garrett Moon on 12/11/15.
6+
// Copyright © 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import <Foundation/Foundation.h>
10+
11+
@interface ASMainSerialQueue : NSObject
12+
13+
- (void)performBlockOnMainThread:(dispatch_block_t)block;
14+
15+
@end
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
//
2+
// ASMainSerialQueue.m
3+
// AsyncDisplayKit
4+
//
5+
// Created by Garrett Moon on 12/11/15.
6+
// Copyright © 2015 Facebook. All rights reserved.
7+
//
8+
9+
#import "ASMainSerialQueue.h"
10+
11+
#import "ASThread.h"
12+
13+
@interface ASMainSerialQueue ()
14+
{
15+
ASDN::Mutex _serialQueueLock;
16+
NSMutableArray *_blocks;
17+
}
18+
19+
@end
20+
21+
@implementation ASMainSerialQueue
22+
23+
- (instancetype)init
24+
{
25+
if (!(self = [super init])) {
26+
return nil;
27+
}
28+
29+
_blocks = [[NSMutableArray alloc] init];
30+
return self;
31+
}
32+
33+
- (void)performBlockOnMainThread:(dispatch_block_t)block
34+
{
35+
ASDN::MutexLocker l(_serialQueueLock);
36+
[_blocks addObject:block];
37+
ASDN::MutexUnlocker u(_serialQueueLock);
38+
[self runBlocks];
39+
}
40+
41+
- (void)runBlocks
42+
{
43+
dispatch_block_t mainThread = ^{
44+
do {
45+
ASDN::MutexLocker l(_serialQueueLock);
46+
dispatch_block_t block;
47+
if (_blocks.count > 0) {
48+
block = [_blocks objectAtIndex:0];
49+
[_blocks removeObjectAtIndex:0];
50+
} else {
51+
break;
52+
}
53+
ASDN::MutexUnlocker u(_serialQueueLock);
54+
block();
55+
} while (true);
56+
};
57+
58+
if ([NSThread isMainThread]) {
59+
mainThread();
60+
} else {
61+
dispatch_async(dispatch_get_main_queue(), ^{
62+
mainThread();
63+
});
64+
}
65+
}
66+
67+
@end

0 commit comments

Comments
 (0)