Skip to content

Commit e10140d

Browse files
committed
Move dispatch call higher up to avoid deadlock on validIndirectObjectsForAction
1 parent 6140df4 commit e10140d

File tree

1 file changed

+34
-26
lines changed

1 file changed

+34
-26
lines changed

Quicksilver/Code-QuickStepInterface/QSInterfaceController.m

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -351,33 +351,41 @@ - (void)updateIndirectObjects {
351351
}
352352
QSAction *aObj = [aSelector objectValue];
353353
id actionProvider = [aObj provider];
354-
NSArray *indirects = nil;
355-
if (actionProvider && [actionProvider respondsToSelector:@selector(validIndirectObjectsForAction:directObject:)]) {
356-
QSObject *directObject = [[dSelector objectValue] resolvedObject];
357-
indirects = [actionProvider validIndirectObjectsForAction:[aObj identifier] directObject:directObject];
358-
}
359-
// If the validIndirectObjectsForAction... method hasn't been implemented, attempt to get valid indirects from the action's 'indirectTypes'
360-
if(!indirects) {
361-
if ([aObj indirectTypes]) {
362-
// Perform on background thread to not block main thread (avoids potential deadlock)
363-
QSGCDAsync(^{
364-
NSMutableArray *indirectsForAllTypes = [[NSMutableArray alloc] initWithCapacity:0];
365-
[[aObj indirectTypes] enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *eachType, NSUInteger idx, BOOL *stop) {
366-
[indirectsForAllTypes addObjectsFromArray:[QSLib scoredArrayForType:eachType]];
367-
}];
368-
NSArray *finalIndirects = ([indirectsForAllTypes count] > 0) ? [indirectsForAllTypes copy] : nil;
369-
370-
// Update UI on main thread
371-
QSGCDMainAsync(^{
372-
[self updateControl:self->iSelector withArray:finalIndirects];
373-
[self->iSelector setSearchMode:(finalIndirects ? SearchFilter : SearchFilterAll)];
374-
});
375-
});
376-
return;
354+
355+
// Capture UI state on the main thread before dispatching
356+
QSObject *directObject = [[dSelector objectValue] resolvedObject];
357+
NSString *actionIdentifier = [aObj identifier];
358+
NSArray *indirectTypes = [aObj indirectTypes];
359+
BOOL hasProvider = (actionProvider && [actionProvider respondsToSelector:@selector(validIndirectObjectsForAction:directObject:)]);
360+
361+
// Perform on background thread to avoid blocking main thread and prevent deadlock
362+
// when action providers use concurrent enumeration that may dispatch_sync back to main
363+
QSGCDAsync(^{
364+
NSArray *indirects = nil;
365+
366+
if (hasProvider) {
367+
indirects = [actionProvider validIndirectObjectsForAction:actionIdentifier directObject:directObject];
377368
}
378-
}
379-
[self updateControl:iSelector withArray:indirects];
380-
[iSelector setSearchMode:(indirects?SearchFilter:SearchFilterAll)];
369+
370+
// If the validIndirectObjectsForAction... method hasn't been implemented or returned nil,
371+
// attempt to get valid indirects from the action's 'indirectTypes'
372+
if (!indirects && indirectTypes) {
373+
NSMutableArray *indirectsForAllTypes = [[NSMutableArray alloc] initWithCapacity:0];
374+
[indirectTypes enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSString *eachType, NSUInteger idx, BOOL *stop) {
375+
[indirectsForAllTypes addObjectsFromArray:[QSLib scoredArrayForType:eachType]];
376+
}];
377+
if ([indirectsForAllTypes count]) {
378+
indirects = [indirectsForAllTypes copy];
379+
}
380+
}
381+
382+
NSArray *finalIndirects = indirects;
383+
// Update UI on main thread
384+
QSGCDMainAsync(^{
385+
[self updateControl:self->iSelector withArray:finalIndirects];
386+
[self->iSelector setSearchMode:(finalIndirects ? SearchFilter : SearchFilterAll)];
387+
});
388+
});
381389
}
382390

383391
- (void)updateViewLocations {

0 commit comments

Comments
 (0)