mirror of
https://github.com/kennethreitz-archive/gitx.git
synced 2026-06-05 23:40:18 +00:00
8fde62f1b0
There have been numerous bug reports about zombie processes in GitX, because if an NSThread dies before an NSTask is finished it won't kill the child process properly. We tried to fix this by adding an [NSTask waitUntilExit] to wait for the process to die, fixing the problem with the zombies. However, this caused another problem: rendering glitches in the history view. The problem is that [NSTask waitUntilExit] does NOT wait until the task has finished (yeah, took me some time to figure that one out). Instead, it runs the main loop and periodically queries if the task has finished yet. What happens then is that during the drawing of one of the cells a call is made to [repository headRef] to determine whether we should show the cell in bold or not. [repository headRef] then invokes an NSTask to figure out what the HEAD ref really is. This in turn runs the [NSTask waitUntilExit]. Now, rather than really wait, the NSTask continues the main loop, and Cocoa tries to display the next cell. This cell again calls an NSTask, etc... So, what we basically have then is that halfway through the NSCell's drawing code, the NSCell will be asked to draw another cell. It then changes the data members and calls the [drawInRect] stuff again. the cell finishes drawing, and control is returned to the previous draw. This path happily continues, but now its data members are changed because of the second draw, so it continues drawing something different than what it started with. As you might imagine, it took me some time to figure that one out :) The next part is to fix it, of course, which might be somewhat tougher. There's the easy fix to only [waitUntilExit] if the current thread is not the main thread. This fixes our problem because nothing is drawn in other threads, and nothing is zombie'd if it's called from the main thread. Another way is to fix the drawing code not to use data members. That way the drawing can continue happily as if nothing ever happend A third way is to fix the drawing part to never execute a git call. I think the first and third way are OK to do. The second option doesn't look very entertaining or practical. Of course, the first option is only working around the problem, and only solves this particular case. I guess this is one of those cases of "I'll work around it this time.. if it bugs me later, I'll fix it properly".
123 lines
3.4 KiB
Objective-C
123 lines
3.4 KiB
Objective-C
//
|
|
// PBEasyPipe.m
|
|
// GitX
|
|
//
|
|
// Created by Pieter de Bie on 16-06-08.
|
|
// Copyright 2008 __MyCompanyName__. All rights reserved.
|
|
//
|
|
|
|
#import "PBEasyPipe.h"
|
|
|
|
|
|
@implementation PBEasyPipe
|
|
|
|
+ (NSFileHandle*) handleForCommand: (NSString*) cmd withArgs: (NSArray*) args
|
|
{
|
|
return [self handleForCommand:cmd withArgs:args inDir:nil];
|
|
}
|
|
|
|
+ (NSTask *) taskForCommand:(NSString *)cmd withArgs:(NSArray *)args inDir:(NSString *)dir
|
|
{
|
|
NSTask* task = [[NSTask alloc] init];
|
|
task.launchPath = cmd;
|
|
task.arguments = args;
|
|
if (dir)
|
|
task.currentDirectoryPath = dir;
|
|
|
|
if ([[NSUserDefaults standardUserDefaults] boolForKey:@"Show Debug Messages"])
|
|
NSLog(@"Starting command `%@ %@` in dir %@", cmd, [args componentsJoinedByString:@" "], dir);
|
|
#ifdef CLI
|
|
NSLog(@"Starting command `%@ %@` in dir %@", cmd, [args componentsJoinedByString:@" "], dir);
|
|
#endif
|
|
|
|
NSPipe* pipe = [NSPipe pipe];
|
|
task.standardOutput = pipe;
|
|
return task;
|
|
}
|
|
|
|
+ (NSFileHandle*) handleForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir: (NSString*) dir
|
|
{
|
|
NSTask *task = [self taskForCommand:cmd withArgs:args inDir:dir];
|
|
NSFileHandle* handle = [task.standardOutput fileHandleForReading];
|
|
|
|
[task launch];
|
|
return handle;
|
|
}
|
|
|
|
|
|
|
|
+ (NSString*) outputForCommand:(NSString *) cmd
|
|
withArgs:(NSArray *) args
|
|
inDir:(NSString *) dir
|
|
retValue:(int *) ret
|
|
{
|
|
return [self outputForCommand:cmd withArgs:args inDir:dir inputString:NULL retValue:ret];
|
|
}
|
|
|
|
// TODO: Refactor this to use the function above
|
|
+ (NSString*) outputForCommand:(NSString *) cmd
|
|
withArgs:(NSArray *) args
|
|
inDir:(NSString *) dir
|
|
inputString:(NSString *)input
|
|
retValue:(int *) ret
|
|
{
|
|
NSTask *task = [self taskForCommand:cmd withArgs:args inDir:dir];
|
|
NSFileHandle* handle = [task.standardOutput fileHandleForReading];
|
|
|
|
if (input) {
|
|
task.standardInput = [NSPipe pipe];
|
|
NSFileHandle *inHandle = [task.standardInput fileHandleForWriting];
|
|
[inHandle writeData:[input dataUsingEncoding:NSUTF8StringEncoding]];
|
|
[inHandle closeFile];
|
|
}
|
|
|
|
[task launch];
|
|
|
|
NSData* data = [handle readDataToEndOfFile];
|
|
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
if (!string)
|
|
string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
|
|
|
|
// Strip trailing newline
|
|
if ([string hasSuffix:@"\n"])
|
|
string = [string substringToIndex:[string length]-1];
|
|
|
|
[task waitUntilExit];
|
|
if (ret)
|
|
*ret = [task terminationStatus];
|
|
return string;
|
|
}
|
|
|
|
// We don't use the above function because then we'd have to wait until the program was finished
|
|
// with running
|
|
|
|
+ (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args inDir: (NSString*) dir
|
|
{
|
|
NSTask *task = [self taskForCommand:cmd withArgs:args inDir:dir];
|
|
NSFileHandle* handle = [task.standardOutput fileHandleForReading];
|
|
|
|
[task launch];
|
|
#warning This can cause a "Bad file descriptor"... when?
|
|
NSData* data = [handle readDataToEndOfFile];
|
|
NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
|
|
if (!string)
|
|
string = [[NSString alloc] initWithData:data encoding:NSISOLatin1StringEncoding];
|
|
|
|
// Strip trailing newline
|
|
if ([string hasSuffix:@"\n"])
|
|
string = [string substringToIndex:[string length]-1];
|
|
|
|
if ([NSThread currentThread] != [NSThread mainThread])
|
|
[task waitUntilExit];
|
|
|
|
return string;
|
|
}
|
|
|
|
|
|
+ (NSString*) outputForCommand: (NSString*) cmd withArgs: (NSArray*) args
|
|
{
|
|
return [self outputForCommand:cmd withArgs:args inDir:nil];
|
|
}
|
|
|
|
@end
|