Skip to content

Commit 1aef490

Browse files
committed
Switch to using NSOperation and NSOperationQueue in PBGitRevList
Use dispatch instead of performSelectorOnMainThread: This prevents multiple loadRevisions from being called at once, which could crash when trying to reload a large repository multiple times rapidly. Now only one refresh operation can run at a time.
1 parent 241e39c commit 1aef490

File tree

3 files changed

+57
-52
lines changed

3 files changed

+57
-52
lines changed

Classes/git/PBGitHistoryList.m

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ - (void) updateCommitsFromGrapher:(NSDictionary *)commitData
135135

136136
- (void) finishedGraphing
137137
{
138-
if (!currentRevList.isParsing && ([[graphQueue operations] count] == 0)) {
138+
if (!currentRevList.parsing && ([[graphQueue operations] count] == 0)) {
139139
self.isUpdating = NO;
140140
}
141141
}

Classes/git/PBGitRevList.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@
1010

1111
@class PBGitRepository;
1212
@class PBGitRevSpecifier;
13+
@class PBGitCommit;
1314

1415
@interface PBGitRevList : NSObject
1516

16-
@property (nonatomic, assign) BOOL isParsing;
17-
@property (nonatomic, strong) NSMutableArray *commits;
17+
@property (nonatomic, assign, getter=isParsing, readonly) BOOL parsing;
18+
@property (nonatomic, strong) NSMutableArray<PBGitCommit *> *commits;
1819

1920
- (id) initWithRepository:(PBGitRepository *)repo rev:(PBGitRevSpecifier *)rev shouldGraph:(BOOL)graph;
2021
- (void) loadRevisons;

Classes/git/PBGitRevList.mm

Lines changed: 53 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,9 @@ @interface PBGitRevList ()
3333
@property (nonatomic, weak) PBGitRepository *repository;
3434
@property (nonatomic, strong) PBGitRevSpecifier *currentRev;
3535

36-
@property (nonatomic, strong) NSMutableDictionary *commitCache;
36+
@property (nonatomic, strong) NSCache<GTOID *, PBGitCommit *> *commitCache;
3737

38-
@property (nonatomic, strong) NSThread *parseThread;
38+
@property (nonatomic, strong) NSOperationQueue *operationQueue;
3939

4040
@end
4141

@@ -54,42 +54,54 @@ - (id) initWithRepository:(PBGitRepository *)repo rev:(PBGitRevSpecifier *)rev s
5454
self.repository = repo;
5555
self.currentRev = [rev copy];
5656
self.isGraphing = graph;
57-
self.commitCache = [NSMutableDictionary new];
57+
self.commitCache = [[NSCache alloc] init];
58+
self.operationQueue = [[NSOperationQueue alloc] init];
59+
self.operationQueue.maxConcurrentOperationCount = 1;
60+
self.operationQueue.qualityOfService = NSQualityOfServiceUtility;
5861

5962
return self;
6063
}
6164

62-
6365
- (void) loadRevisons
6466
{
6567
[self cancel];
66-
67-
self.parseThread = [[NSThread alloc] initWithTarget:self selector:@selector(beginWalkWithSpecifier:) object:self.currentRev];
68-
self.isParsing = YES;
68+
6969
self.resetCommits = YES;
70-
[self.parseThread start];
70+
71+
NSBlockOperation *parseOperation = [[NSBlockOperation alloc] init];
72+
73+
__weak typeof(self) weakSelf = self;
74+
__weak typeof(parseOperation) weakParseOperation = parseOperation;
75+
76+
[parseOperation addExecutionBlock:^{
77+
PBGitRepository *pbRepo = weakSelf.repository;
78+
GTRepository *repo = pbRepo.gtRepo;
79+
80+
NSError *error = nil;
81+
GTEnumerator *enu = [[GTEnumerator alloc] initWithRepository:repo error:&error];
82+
83+
[weakSelf setupEnumerator:enu forRevspec:weakSelf.currentRev];
84+
[weakSelf addCommitsFromEnumerator:enu inPBRepo:pbRepo operation:weakParseOperation];
85+
}];
86+
87+
[self.operationQueue addOperation:parseOperation];
7188
}
7289

7390

7491
- (void)cancel
7592
{
76-
[self.parseThread cancel];
77-
self.parseThread = nil;
78-
self.isParsing = NO;
93+
[self.operationQueue cancelAllOperations];
7994
}
8095

81-
82-
- (void) finishedParsing
96+
- (BOOL)isParsing
8397
{
84-
self.parseThread = nil;
85-
self.isParsing = NO;
98+
return self.operationQueue.operationCount > 0;
8699
}
87100

88101

89-
- (void) updateCommits:(NSDictionary *)update
102+
- (void) updateCommits:(NSArray<PBGitCommit *> *)revisions operation:(NSOperation *)operation
90103
{
91-
NSArray *revisions = [update objectForKey:kRevListRevisionsKey];
92-
if (!revisions || [revisions count] == 0)
104+
if (!revisions || [revisions count] == 0 || operation.cancelled)
93105
return;
94106

95107
if (self.resetCommits) {
@@ -105,19 +117,6 @@ - (void) updateCommits:(NSDictionary *)update
105117
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:@"commits"];
106118
}
107119

108-
- (void) beginWalkWithSpecifier:(PBGitRevSpecifier*)rev
109-
{
110-
PBGitRepository *pbRepo = self.repository;
111-
GTRepository *repo = pbRepo.gtRepo;
112-
113-
NSError *error = nil;
114-
GTEnumerator *enu = [[GTEnumerator alloc] initWithRepository:repo error:&error];
115-
116-
[self setupEnumerator:enu forRevspec:rev];
117-
118-
[self addCommitsFromEnumerator:enu inPBRepo:pbRepo];
119-
}
120-
121120
static BOOL hasParameter(NSMutableArray *parameters, NSString *paramName) {
122121
NSUInteger index = NSNotFound;
123122

@@ -191,8 +190,7 @@ - (void) setupEnumerator:(GTEnumerator*)enumerator
191190

192191
}
193192

194-
- (void) addCommitsFromEnumerator:(GTEnumerator *)enumerator
195-
inPBRepo:(PBGitRepository*)pbRepo;
193+
- (void) addCommitsFromEnumerator:(GTEnumerator *)enumerator inPBRepo:(PBGitRepository*)pbRepo operation:(NSOperation *)operation
196194
{
197195
PBGitGrapher *g = [[PBGitGrapher alloc] initWithRepository:pbRepo];
198196
__block NSDate *lastUpdate = [NSDate date];
@@ -204,11 +202,15 @@ - (void) addCommitsFromEnumerator:(GTEnumerator *)enumerator
204202

205203
BOOL enumSuccess = FALSE;
206204
__block int num = 0;
207-
__block NSMutableArray *revisions = [NSMutableArray array];
205+
__block NSMutableArray<PBGitCommit *> *revisions = [NSMutableArray array];
208206
NSError *enumError = nil;
209207
GTOID *oid = nil;
210-
while ((oid = [enumerator nextOIDWithSuccess:&enumSuccess error:&enumError]) && enumSuccess) {
208+
while ((oid = [enumerator nextOIDWithSuccess:&enumSuccess error:&enumError]) && enumSuccess && !operation.cancelled) {
211209
dispatch_group_async(loadGroup, loadQueue, ^{
210+
if (operation.cancelled) {
211+
return;
212+
}
213+
212214
PBGitCommit *newCommit = nil;
213215
PBGitCommit *cachedCommit = [self.commitCache objectForKey:oid];
214216
if (cachedCommit) {
@@ -231,14 +233,17 @@ - (void) addCommitsFromEnumerator:(GTEnumerator *)enumerator
231233
});
232234
}
233235

234-
if (++num % 100 == 0) {
235-
if ([[NSDate date] timeIntervalSinceDate:lastUpdate] > 0.5 && ![[NSThread currentThread] isCancelled]) {
236-
dispatch_group_wait(decorateGroup, DISPATCH_TIME_FOREVER);
237-
NSDictionary *update = [NSDictionary dictionaryWithObjectsAndKeys:revisions, kRevListRevisionsKey, nil];
238-
[self performSelectorOnMainThread:@selector(updateCommits:) withObject:update waitUntilDone:NO];
239-
revisions = [NSMutableArray array];
240-
lastUpdate = [NSDate date];
241-
}
236+
if (++num % 100 == 0 && [[NSDate date] timeIntervalSinceDate:lastUpdate] > 0.2) {
237+
dispatch_group_wait(decorateGroup, DISPATCH_TIME_FOREVER);
238+
239+
NSArray<PBGitCommit *> *updatedRevisions = [revisions copy];
240+
241+
dispatch_async(dispatch_get_main_queue(), ^{
242+
[self updateCommits:updatedRevisions operation:operation];
243+
});
244+
245+
[revisions removeAllObjects];
246+
lastUpdate = [NSDate date];
242247
}
243248
});
244249
}
@@ -249,12 +254,11 @@ - (void) addCommitsFromEnumerator:(GTEnumerator *)enumerator
249254
dispatch_group_wait(decorateGroup, DISPATCH_TIME_FOREVER);
250255

251256
// Make sure the commits are stored before exiting.
252-
if (![[NSThread currentThread] isCancelled]) {
253-
NSDictionary *update = [NSDictionary dictionaryWithObjectsAndKeys:revisions, kRevListRevisionsKey, nil];
254-
[self performSelectorOnMainThread:@selector(updateCommits:) withObject:update waitUntilDone:YES];
255-
256-
[self performSelectorOnMainThread:@selector(finishedParsing) withObject:nil waitUntilDone:NO];
257-
}
257+
NSArray<PBGitCommit *> *updatedRevisions = [revisions copy];
258+
259+
dispatch_async(dispatch_get_main_queue(), ^{
260+
[self updateCommits:updatedRevisions operation:operation];
261+
});
258262
}
259263

260264
@end

0 commit comments

Comments
 (0)