Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3130071
Move the task wrangling out to PBTask
tiennou Feb 21, 2017
64eea96
Add environment managing to PBTask
tiennou Feb 22, 2017
154be48
Add an `inDirectory` variant to our nicer methods
tiennou Feb 22, 2017
09112fa
Give access to the output data as an UTF-8 string
tiennou Feb 22, 2017
208d327
Stash the task's output in the error when execution fails
tiennou Mar 14, 2017
514c47b
Add a way to schedule blocks on specific queues
tiennou Jun 9, 2017
298288a
Add a -description
tiennou Mar 14, 2017
a30c98a
Actually report whether the task was successful or not
tiennou Mar 14, 2017
9f264c2
Stringify the task's arguments when reporting errors
tiennou Mar 15, 2017
060d377
Blacklist some more env vars
tiennou Jun 7, 2017
c25335a
Fix hang caused by task data output size
tiennou Jun 7, 2017
b55fbb0
Break retain cycle caused by retaining our task inside blocks
tiennou Jun 7, 2017
b69347f
Task debug logging
tiennou Jun 7, 2017
a6318dd
Don't close our read handle if there's still data available
tiennou Jan 29, 2018
ffb8b06
Fix a double-free by synchronizing task reading
tiennou Jan 29, 2018
b8c6eca
WIP: check that we were able to write all our data before closing
tiennou Jan 29, 2018
0b66703
Split the `git`-using methods in a category
tiennou Feb 22, 2017
278cdca
Change a few methods to PBTask
tiennou Feb 22, 2017
3d96347
Import PBTask from the category so it doesn't get sprinkled everywhere
tiennou Mar 14, 2017
87820be
Switch the history search to PBTask
tiennou Mar 14, 2017
cb0684b
More PBTask-ing
tiennou Mar 14, 2017
eee3a31
Switch PBWebController to PBTask
tiennou Jun 9, 2017
09e423c
Switch PBGitXProtocol to PBTask
tiennou Jun 9, 2017
3a1ece5
De-deprecate
tiennou Jan 29, 2018
d9d3cc1
Removed unnecessary #imports of PBEasyPipe.h
ksuther Feb 11, 2018
9a9cf92
Remove extra calls to readDataToEndOfFile that were causing exceptions
ksuther Feb 11, 2018
5cdd1e9
Switch PBRemoteProgressSheet to PBTask
ksuther Feb 11, 2018
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
3 changes: 2 additions & 1 deletion Classes/Controllers/PBHistorySearchController.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
#import "PBHistorySearchMode.h"

@class PBGitHistoryController;
@class PBTask;

@interface PBHistorySearchController : NSObject {
PBHistorySearchMode searchMode;
NSIndexSet *results;
NSTimer *searchTimer;
NSTask *backgroundSearchTask;
PBTask *backgroundSearchTask;
NSPanel *rewindPanel;
}

Expand Down
24 changes: 10 additions & 14 deletions Classes/Controllers/PBHistorySearchController.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,9 @@
#import "PBHistorySearchController.h"
#import "PBGitHistoryController.h"
#import "PBGitRepository.h"
#import "PBGitRepository_PBGitBinarySupport.h"
#import "PBGitDefaults.h"
#import "PBCommitList.h"
#import "PBEasyPipe.h"
#import "PBGitBinary.h"
#import "PBGitCommit.h"

@interface PBHistorySearchController ()
Expand Down Expand Up @@ -394,8 +393,6 @@ - (void)startBasicSearch
- (void)startBackgroundSearch
{
if (backgroundSearchTask) {
NSFileHandle *handle = [[backgroundSearchTask standardOutput] fileHandleForReading];
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
[backgroundSearchTask terminate];
}

Expand All @@ -422,23 +419,22 @@ - (void)startBackgroundSearch
return;
}

backgroundSearchTask = [PBEasyPipe taskForCommand:[PBGitBinary path] withArgs:searchArguments inDir:[[historyController.repository workingDirectoryURL] path]];
[backgroundSearchTask launch];

NSFileHandle *handle = [[backgroundSearchTask standardOutput] fileHandleForReading];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(parseBackgroundSearchResults:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
[handle readToEndOfFileInBackgroundAndNotify];
backgroundSearchTask = [historyController.repository taskWithArguments:searchArguments];
[backgroundSearchTask performTaskWithCompletionHandler:^(NSData * _Nullable readData, NSError * _Nullable error) {
if (!readData) {
[historyController.windowController showErrorSheet:error];
return;
}
[self parseBackgroundSearchResults:readData];
}];

[self startProgressIndicator];
}

- (void)parseBackgroundSearchResults:(NSNotification *)notification
- (void)parseBackgroundSearchResults:(NSData *)data
{
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSFileHandleReadToEndOfFileCompletionNotification object:[notification object]];
backgroundSearchTask = nil;

NSData *data = [[notification userInfo] valueForKey:NSFileHandleNotificationDataItem];

NSString *resultsString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSArray *resultsArray = [resultsString componentsSeparatedByString:@"\n"];

Expand Down
1 change: 0 additions & 1 deletion Classes/Controllers/PBRepositoryDocumentController.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#import "PBRepositoryDocumentController.h"
#import "PBGitRepositoryDocument.h"
#import "PBGitRevList.h"
#import "PBEasyPipe.h"
#import "PBGitBinary.h"

#import <ObjectiveGit/GTRepository.h>
Expand Down
1 change: 1 addition & 0 deletions Classes/Controllers/PBServicesController.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#import "PBServicesController.h"
#import "PBGitRepositoryDocument.h"
#import "PBGitRepository.h"
#import "PBGitRepository_PBGitBinarySupport.h"

@implementation PBServicesController

Expand Down
37 changes: 10 additions & 27 deletions Classes/Controllers/PBWebController.m
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#import "PBWebController.h"
#import "PBGitRepository.h"
#import "PBGitRepository_PBGitBinarySupport.h"
#import "PBGitXProtocol.h"
#import "PBGitDefaults.h"

Expand Down Expand Up @@ -180,33 +181,15 @@ - (void) runCommand:(WebScriptObject *)arguments inRepository:(PBGitRepository *
for (i = 0; i < length; i++)
[realArguments addObject:[arguments webScriptValueAtIndex:i]];

NSFileHandle *handle = [repo handleInWorkDirForArguments:realArguments];
[callbacks setObject:callBack forKey:handle];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(JSRunCommandDone:) name:NSFileHandleReadToEndOfFileCompletionNotification object:handle];
[handle readToEndOfFileInBackgroundAndNotify];
}

- (void) returnCallBackForObject:(id)object withData:(id)data
{
WebScriptObject *a = [callbacks objectForKey: object];
if (!a) {
NSLog(@"Could not find a callback for object: %@", object);
return;
}

[callbacks removeObjectForKey:object];
[a callWebScriptMethod:@"call" withArguments:[NSArray arrayWithObjects:@"", data, nil]];
}

- (void) threadFinished:(NSArray *)arguments
{
[self returnCallBackForObject:[arguments objectAtIndex:0] withData:[arguments objectAtIndex:1]];
}

- (void) JSRunCommandDone:(NSNotification *)notification
{
NSString *data = [[NSString alloc] initWithData:[[notification userInfo] valueForKey:NSFileHandleNotificationDataItem] encoding:NSUTF8StringEncoding];
[self returnCallBackForObject:[notification object] withData:data];
PBTask *task = [repo taskWithArguments:realArguments];
[task performTaskWithCompletionHandler:^(NSData * _Nullable readData, NSError * _Nullable error) {
if (error) {
/* FIXME: Might want to inform the JS that something went wrong */
NSLog(@"error: %@", error);
return;
}
[callBack callWebScriptMethod:@"call" withArguments:@[@"", readData]];
}];
}

- (void) preferencesChanged
Expand Down
1 change: 0 additions & 1 deletion Classes/PBChangedFile.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
//

#import "PBChangedFile.h"
#import "PBEasyPipe.h"

@implementation PBChangedFile

Expand Down
21 changes: 8 additions & 13 deletions Classes/Util/NSApplication+GitXScripting.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#import "PBGitRepository.h"
#import "PBCloneRepositoryPanel.h"
#import "PBGitBinary.h"
#import "PBEasyPipe.h"
#import "PBTask.h"


@implementation NSApplication (GitXScripting)
Expand All @@ -31,23 +31,18 @@ - (void)performDiffScriptCommand:(NSScriptCommand *)command
NSURL *repositoryURL = command.directParameter;
NSArray *diffOptions = command.arguments[@"diffOptions"];

diffOptions = [[NSArray arrayWithObjects:@"diff", @"--no-ext-diff", nil] arrayByAddingObjectsFromArray:diffOptions];
diffOptions = [@[@"diff", @"--no-ext-diff"] arrayByAddingObjectsFromArray:diffOptions];

int retValue = 1;
NSString *diffOutput = [PBEasyPipe outputForCommand:[PBGitBinary path] withArgs:diffOptions inDir:[repositoryURL path] retValue:&retValue];
if (retValue) {
NSError *error = nil;
NSString *diffOutput = [PBTask outputForCommand:[PBGitBinary path] arguments:diffOptions inDirectory:repositoryURL.path error:&error];
if (!diffOutput) {
// if there is an error diffOutput should have the error output from git
if (diffOutput)
NSLog(@"%s\n", [diffOutput UTF8String]);
else
NSLog(@"Invalid diff command [%d]\n", retValue);
NSLog(@"Invalid diff command: %@", error);
return;
}

if (diffOutput) {
PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:diffOutput];
[diffController showWindow:self];
}
PBDiffWindowController *diffController = [[PBDiffWindowController alloc] initWithDiff:diffOutput];
[diffController showWindow:self];
}

- (void)initRepositoryScriptCommand:(NSScriptCommand *)command
Expand Down
49 changes: 0 additions & 49 deletions Classes/Util/PBEasyPipe.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,57 +10,8 @@

NS_ASSUME_NONNULL_BEGIN

extern NSString *const PBEasyPipeErrorDomain;
extern NSString *const PBEasyPipeUnderlyingExceptionKey;
extern NSString *const PBEasyPipeTerminationStatusKey;
extern NSString *const PBEasyPipeTerminationOutputKey;

typedef NS_ENUM(NSUInteger, PBEasyPipeError) {
PBEasyPipeTaskLaunchError = 1,
PBEasyPipeTaskTimeoutError = 2,
PBEasyPipeTaskCaughtSignalError = 3,
PBEasyPipeTaskNonZeroExitCodeError = 4,
};



@interface PBEasyPipe: NSObject

///
/// Execute a command in the shell.
///
/// This is a wrapper around NSTask that uses blocks to report when the
/// executable exits, and should be used whenever we need to shell out to git.
///
/// @param command The absolute path to the executable that should be run.
/// @param arguments The arguments to pass to the executable.
/// @param directory The directory to use as the executable working directory.
/// @param terminationHandler A block that will be called when the executable exits.
///
+ (nullable NSTask *)performCommand:(NSString *)command arguments:(nullable NSArray *)arguments inDirectory:(nullable NSString *)directory terminationHandler:(void (^)(NSTask *task, NSError * __nullable error))terminationHandler;

///
/// Execute a command in the shell, and process its output.
///
/// @see -performCommand:arguments:inDirectory:terminationHandler:
///
/// @param command The absolute path to the executable that should be run.
/// @param arguments The arguments to pass to the executable.
/// @param directory The directory to use as the executable working directory.
/// @param completionHandler A block that will be called when the executable exits.
/// If readData is nil, it means an error occurred.
///
+ (nullable NSTask *)performCommand:(NSString *)command arguments:(nullable NSArray *)arguments inDirectory:(nullable NSString *)directory completionHandler:(void (^)(NSTask *task, NSData * __nullable readData, NSError * __nullable error))completionHandler;

///
/// Setup a task for subsequent execution.
///
/// @param command The absolute path to the executable that should be run.
/// @param arguments The arguments to pass to the executable. Can be nil.
/// @param directory The directory to use as the executable working directory. Can be nil.
///
+ (NSTask *)taskForCommand:(NSString *)command arguments:(nullable NSArray *)arguments inDirectory:(nullable NSString *)directory;

/* The following methods are kept for backward-compatibility.
* Newly-written code should use the block-based methods above.
*/
Expand Down
88 changes: 0 additions & 88 deletions Classes/Util/PBEasyPipe.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,99 +7,11 @@
//

#import "PBEasyPipe.h"
#import <objc/runtime.h>

NSString *const PBEasyPipeErrorDomain = @"PBEasyPipeErrorDomain";
NSString *const PBEasyPipeUnderlyingExceptionKey = @"PBEasyPipeUnderlyingExceptionKey";

#pragma mark - NSPipe category

/* Some NSTask's generate a lot of output. Using a regular pipe with these tasks causes the pipe to fill up before the
task completes; it will basically fail silently. To fix this, the pipe must occasionally be read into a
cache, but because this can occur at the end of a chain of async operations, it gets complicated. Instead, you can set
the .dataOutput property on this NSPipe category, and the intermittent results automatically will be read into
that NSMutableData.
*/

@interface NSPipe (PBEasyPipe)

@property (nonatomic, strong) NSMutableData *dataOutput;

@end

@implementation NSPipe (PBEasyPipe)

- (void)setDataOutput:(nonnull NSMutableData *)dataOutput {
objc_setAssociatedObject(self, @selector(dataOutput), dataOutput, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

NSFileHandle *fileHandle = self.fileHandleForReading;
NSAssert(fileHandle != nil, @"should have file handle for reading");

fileHandle.readabilityHandler = ^(NSFileHandle * _Nonnull fileHandle) {
[dataOutput appendData:fileHandle.availableData];
[fileHandle closeFile];
};
}

- (NSMutableData *)dataOutput {
return objc_getAssociatedObject(self, @selector(dataOutput));
}

- (void)clear {
self.fileHandleForReading.readabilityHandler = nil;
}

@end

#pragma mark - PBEasyPipe implementation

@implementation PBEasyPipe

+ (nullable NSTask *)performCommand:(NSString *)command arguments:(NSArray *)arguments inDirectory:(NSString *)directory terminationHandler:(void (^)(NSTask *, NSError *error))terminationHandler {
NSParameterAssert(command != nil);

NSTask *task = [self taskForCommand:command arguments:arguments inDirectory:directory];
NSPipe *pipe = task.standardOutput;
pipe.dataOutput = [NSMutableData data];

task.terminationHandler = ^(NSTask *task) {
dispatch_async(dispatch_get_main_queue(), ^{
terminationHandler(task, nil);
});
};

@try {
[task launch];
} @catch (NSException *exception) {
NSString *desc = @"Exception raised while launching task";
NSString *failureReason = [NSString stringWithFormat:@"The task \"%@\" failed to launch", command];
NSDictionary *info = @{NSLocalizedDescriptionKey: desc,
NSLocalizedFailureReasonErrorKey: failureReason,
PBEasyPipeUnderlyingExceptionKey: exception};
NSError *error = [NSError errorWithDomain:PBEasyPipeErrorDomain code:PBEasyPipeTaskLaunchError userInfo:info];
terminationHandler(task, error);
}

return task;
}

+ (nullable NSTask *)performCommand:(NSString *)command arguments:(NSArray *)arguments inDirectory:(NSString *)directory completionHandler:(void (^)(NSTask *, NSData *readData, NSError *error))completionHandler {

return [self performCommand:command arguments:arguments inDirectory:directory terminationHandler:^(NSTask *task, NSError *error) {
NSPipe *pipe = task.standardOutput;

if (error) {
[pipe clear];
completionHandler(task, nil, error);
return;
}

[pipe clear];

completionHandler(task, pipe.dataOutput, nil);
}];
}

+ (NSTask *)taskForCommand:(NSString *)cmd arguments:(NSArray *)args inDirectory:(NSString *)directory
{
NSTask *task = [[NSTask alloc] init];
Expand Down
Loading