Skip to content
Snippets Groups Projects
Commit c9e3fc42 authored by George Nachman's avatar George Nachman
Browse files

Fix coprocesses on tmux sessions

parent 6e606eb0
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -1793,6 +1793,13 @@ static NSTimeInterval kMinimumPartialLineTriggerCheckInterval = 0.5;
alternateSemantics:savedBgColor.backgroundColorMode == ColorModeAlternate];
}
 
- (void)writeForCoprocessOnlyTask:(NSData *)data {
// The if statement is just a sanity check.
if (self.tmuxMode == TMUX_CLIENT) {
[self writeTask:data];
}
}
- (void)threadedTaskBrokenPipe
{
// Put the call to brokenPipe in the same queue as executeTokens:bytesHandled: to avoid a race.
Loading
Loading
@@ -3856,10 +3863,10 @@ static NSTimeInterval kMinimumPartialLineTriggerCheckInterval = 0.5;
[_tmuxGateway detach];
}
 
- (void)setTmuxPane:(int)windowPane
{
- (void)setTmuxPane:(int)windowPane {
_tmuxPane = windowPane;
self.tmuxMode = TMUX_CLIENT;
[_shell registerAsCoprocessOnlyTask];
}
 
- (void)toggleTmuxZoom {
Loading
Loading
@@ -4094,8 +4101,7 @@ static NSTimeInterval kMinimumPartialLineTriggerCheckInterval = 0.5;
_tmuxSecureLogging = secureLogging;
}
 
- (void)tmuxWriteData:(NSData *)data
{
- (void)tmuxWriteData:(NSData *)data {
if (_exited) {
return;
}
Loading
Loading
@@ -4134,6 +4140,9 @@ static NSTimeInterval kMinimumPartialLineTriggerCheckInterval = 0.5;
[data release];
[self release];
});
if (_shell.coprocess) {
[_shell writeToCoprocessOnlyTask:data];
}
}
}
 
Loading
Loading
Loading
Loading
@@ -16,6 +16,7 @@ extern NSString *kCoprocessStatusChangeNotification;
- (void)threadedTaskBrokenPipe;
- (void)brokenPipe; // Called in main thread
- (void)taskWasDeregistered;
- (void)writeForCoprocessOnlyTask:(NSData *)data;
@end
 
@interface PTYTask : NSObject
Loading
Loading
@@ -28,6 +29,11 @@ extern NSString *kCoprocessStatusChangeNotification;
@property(nonatomic, readonly) BOOL pidIsChild;
@property(nonatomic, readonly) pid_t serverPid;
 
// Tmux sessions are coprocess-only tasks. They have no file descriptor or pid,
// but they may have a coprocess that needs TaskNotifier to read, write, and wait on.
@property(atomic, assign) BOOL isCoprocessOnly;
@property(atomic, readonly) BOOL coprocessOnlyTaskIsDead;
- (id)init;
- (void)dealloc;
- (BOOL)hasBrokenPipe;
Loading
Loading
@@ -87,5 +93,8 @@ extern NSString *kCoprocessStatusChangeNotification;
serverProcessId:(pid_t)serverPid
childProcessId:(pid_t)childPid;
 
- (void)registerAsCoprocessOnlyTask;
- (void)writeToCoprocessOnlyTask:(NSData *)data;
@end
 
Loading
Loading
@@ -77,6 +77,7 @@ setup_tty_param(struct termios* term,
 
@interface PTYTask ()
@property(atomic, assign) BOOL hasMuteCoprocess;
@property(atomic, assign) BOOL coprocessOnlyTaskIsDead;
@end
 
@implementation PTYTask {
Loading
Loading
@@ -455,6 +456,26 @@ static int MyForkPty(int *amaster,
}
}
 
- (void)registerAsCoprocessOnlyTask {
self.isCoprocessOnly = YES;
[[TaskNotifier sharedInstance] registerTask:self];
}
- (void)writeToCoprocessOnlyTask:(NSData *)data {
if (self.coprocess) {
TaskNotifier *taskNotifier = [TaskNotifier sharedInstance];
[taskNotifier lock];
@synchronized (self) {
[self.coprocess.outputBuffer appendData:data];
}
[taskNotifier unlock];
// Wake up the task notifier so the coprocess's output buffer will be sent to its file
// descriptor.
[taskNotifier unblock];
}
}
- (BOOL)wantsRead {
return !self.paused;
}
Loading
Loading
@@ -578,12 +599,23 @@ static int MyForkPty(int *amaster,
 
- (void)writeTask:(NSData*)data
{
// Write as much as we can now through the non-blocking pipe
// Lock to protect the writeBuffer from the IO thread
[writeLock lock];
[writeBuffer appendData:data];
[[TaskNotifier sharedInstance] unblock];
[writeLock unlock];
if (self.isCoprocessOnly) {
// Send keypresses to tmux.
[_delegate retain];
NSData *copyOfData = [data copy];
dispatch_async(dispatch_get_main_queue(), ^{
[_delegate writeForCoprocessOnlyTask:copyOfData];
[_delegate release];
[copyOfData release];
});
} else {
// Write as much as we can now through the non-blocking pipe
// Lock to protect the writeBuffer from the IO thread
[writeLock lock];
[writeBuffer appendData:data];
[[TaskNotifier sharedInstance] unblock];
[writeLock unlock];
}
}
 
- (void)brokenPipe {
Loading
Loading
@@ -672,6 +704,9 @@ static int MyForkPty(int *amaster,
// the select thread wouldn't know which task a fd belongs to.
fd = -1;
}
if (self.isCoprocessOnly) {
self.coprocessOnlyTaskIsDead = YES;
}
}
 
// This runs in TaskNotifier's thread.
Loading
Loading
Loading
Loading
@@ -24,4 +24,7 @@ extern NSString *const kTaskNotifierDidSpin;
 
- (void)notifyCoprocessChange;
 
- (void)lock;
- (void)unlock;
@end
Loading
Loading
@@ -17,10 +17,11 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
 
@implementation TaskNotifier
{
NSMutableArray* tasks;
// Set to true when an element of 'tasks' was modified
NSMutableArray *_tasks;
NSMutableArray *_coprocessOnlyTasks;
// Set to true when an element of '_tasks' was modified
BOOL tasksChanged;
// Protects 'tasks' and 'tasksChanged'.
// Protects '_tasks', '_coprocessOnlyTasks', and 'tasksChanged'.
NSRecursiveLock* tasksLock;
 
// A set of NSNumber*s holding pids of tasks that need to be wait()ed on
Loading
Loading
@@ -47,7 +48,8 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
self = [super init];
if (self) {
deadpool = [[NSMutableSet alloc] init];
tasks = [[NSMutableArray alloc] init];
_tasks = [[NSMutableArray alloc] init];
_coprocessOnlyTasks = [[NSMutableArray alloc] init];
tasksLock = [[NSRecursiveLock alloc] init];
tasksChanged = NO;
 
Loading
Loading
@@ -71,7 +73,8 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
 
- (void)dealloc
{
[tasks release];
[_tasks release];
[_coprocessOnlyTasks release];
[tasksLock release];
[deadpool release];
close(unblockPipeR);
Loading
Loading
@@ -83,16 +86,19 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
PtyTaskDebugLog(@"registerTask: lock\n");
[tasksLock lock];
PtyTaskDebugLog(@"Add task at %p\n", (void*)task);
[tasks addObject:task];
PtyTaskDebugLog(@"There are now %lu tasks\n", (unsigned long)[tasks count]);
if (task.isCoprocessOnly) {
[_coprocessOnlyTasks addObject:task];
} else {
[_tasks addObject:task];
}
PtyTaskDebugLog(@"There are now %lu tasks\n", (unsigned long)_tasks.count);
tasksChanged = YES;
PtyTaskDebugLog(@"registerTask: unlock\n");
[tasksLock unlock];
[self unblock];
}
 
- (void)deregisterTask:(PTYTask *)task
{
- (void)deregisterTask:(PTYTask *)task {
PtyTaskDebugLog(@"deregisterTask: lock\n");
[tasksLock lock];
PtyTaskDebugLog(@"Begin remove task %p\n", (void*)task);
Loading
Loading
@@ -108,10 +114,13 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
if ([task hasCoprocess]) {
[deadpool addObject:@([[task coprocess] pid])];
}
[tasks removeObject:task];
[_tasks removeObject:task];
[_coprocessOnlyTasks removeObject:task];
tasksChanged = YES;
PtyTaskDebugLog(@"End remove task %p. There are now %lu tasks.\n",
(void*)task, (unsigned long)[tasks count]);
PtyTaskDebugLog(@"End remove task %p. There are now %lu tasks and %ld coprocess-only tasks.\n",
(void*)task,
(unsigned long)[_tasks count],
(unsigned long)_coprocessOnlyTasks.count);
PtyTaskDebugLog(@"deregisterTask: unlock\n");
[tasksLock unlock];
[self unblock];
Loading
Loading
@@ -188,6 +197,7 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
withCoprocess:(Coprocess *)coprocess
fdSet:(fd_set *)fdSet {
if (![coprocess eof] && FD_ISSET(fd, fdSet)) {
PtyTaskDebugLog(@"Reading from coprocess");
[coprocess read];
[task writeTask:coprocess.inputBuffer];
[coprocess.inputBuffer setLength:0];
Loading
Loading
@@ -198,6 +208,7 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
withCoprocess:(Coprocess *)coprocess
fdSet:(fd_set *)fdSet {
if (FD_ISSET(fd, fdSet)) {
PtyTaskDebugLog(@"EOF on coprocess %@", coprocess);
coprocess.eof = YES;
}
}
Loading
Loading
@@ -207,6 +218,7 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
fdSet:(fd_set *)fdSet {
if (FD_ISSET(coprocessWriteFd, fdSet)) {
if (![coprocess eof]) {
PtyTaskDebugLog(@"Write to coprocess %@", coprocess);
[coprocess write];
}
}
Loading
Loading
@@ -219,7 +231,6 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
fd_set efds;
int highfd;
NSEnumerator* iter;
PTYTask* task;
NSAutoreleasePool* autoreleasePool = [[NSAutoreleasePool alloc] init];
 
// FIXME: replace this with something better...
Loading
Loading
@@ -232,17 +243,20 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
// Unblock pipe to interrupt select() whenever a PTYTask register/unregisters
highfd = unblockPipeR;
FD_SET(unblockPipeR, &rfds);
NSMutableSet* handledFds = [[NSMutableSet alloc] initWithCapacity:[tasks count]];
NSMutableSet* handledFds = [[NSMutableSet alloc] initWithCapacity:_tasks.count];
 
// Add all the PTYTask pipes
PtyTaskDebugLog(@"run1: lock");
[tasksLock lock];
PtyTaskDebugLog(@"Begin cleaning out dead tasks");
int j;
for (j = [tasks count] - 1; j >= 0; --j) {
PTYTask* theTask = [tasks objectAtIndex:j];
for (PTYTask *theTask in _tasks) {
if ([theTask fd] < 0) {
PtyTaskDebugLog(@"Deregister dead task %d\n", j);
PtyTaskDebugLog(@"Deregister dead task %@\n", theTask);
[self deregisterTask:theTask];
}
}
for (PTYTask *theTask in _coprocessOnlyTasks) {
if ([theTask coprocessOnlyTaskIsDead]) {
[self deregisterTask:theTask];
}
}
Loading
Loading
@@ -273,57 +287,55 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
deadpool = [newDeadpool retain];
}
 
PtyTaskDebugLog(@"Begin enumeration over %lu tasks\n", (unsigned long)[tasks count]);
iter = [tasks objectEnumerator];
int i = 0;
// FIXME: this can be converted to ObjC 2.0.
while ((task = [iter nextObject])) {
PtyTaskDebugLog(@"Got task %d\n", i);
int fd = [task fd];
if (fd < 0) {
PtyTaskDebugLog(@"Task has fd of %d\n", fd);
} else {
// PtyTaskDebugLog(@"Select on fd %d\n", fd);
if (fd > highfd) {
highfd = fd;
}
if ([task wantsRead]) {
FD_SET(fd, &rfds);
}
if ([task wantsWrite]) {
FD_SET(fd, &wfds);
// Figure out the file descriptors to select on.
PtyTaskDebugLog(@"Begin enumeration over %lu tasks\n", (unsigned long)[_tasks count]);
for (NSArray *taskArray in @[ _tasks, _coprocessOnlyTasks ]) {
for (PTYTask *task in taskArray) {
PtyTaskDebugLog(@"Got task %@\n", task);
int fd = [task fd];
if (fd < 0) {
PtyTaskDebugLog(@"Task has fd of %d\n", fd);
} else {
// PtyTaskDebugLog(@"Select on fd %d\n", fd);
if (fd > highfd) {
highfd = fd;
}
if ([task wantsRead]) {
FD_SET(fd, &rfds);
}
if ([task wantsWrite]) {
FD_SET(fd, &wfds);
}
FD_SET(fd, &efds);
}
FD_SET(fd, &efds);
}
 
@synchronized (task) {
Coprocess *coprocess = [task coprocess];
if (coprocess) {
if ([coprocess wantToRead] && [task writeBufferHasRoom]) {
int rfd = [coprocess readFileDescriptor];
if (rfd > highfd) {
highfd = rfd;
@synchronized (task) {
Coprocess *coprocess = [task coprocess];
if (coprocess) {
if ([coprocess wantToRead] && [task writeBufferHasRoom]) {
int rfd = [coprocess readFileDescriptor];
if (rfd > highfd) {
highfd = rfd;
}
FD_SET(rfd, &rfds);
}
FD_SET(rfd, &rfds);
}
if ([coprocess wantToWrite]) {
int wfd = [coprocess writeFileDescriptor];
if (wfd > highfd) {
highfd = wfd;
if ([coprocess wantToWrite]) {
int wfd = [coprocess writeFileDescriptor];
if (wfd > highfd) {
highfd = wfd;
}
FD_SET(wfd, &wfds);
}
FD_SET(wfd, &wfds);
}
if (![coprocess eof]) {
int rfd = [coprocess readFileDescriptor];
if (rfd > highfd) {
highfd = rfd;
if (![coprocess eof]) {
int rfd = [coprocess readFileDescriptor];
if (rfd > highfd) {
highfd = rfd;
}
FD_SET(rfd, &efds);
}
FD_SET(rfd, &efds);
}
}
}
++i;
PtyTaskDebugLog(@"About to get task %d\n", i);
}
PtyTaskDebugLog(@"run1: unlock");
[tasksLock unlock];
Loading
Loading
@@ -352,13 +364,13 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
// Check for read events on PTYTask pipes
PtyTaskDebugLog(@"run2: lock");
[tasksLock lock];
PtyTaskDebugLog(@"Iterating over %lu tasks\n", (unsigned long)[tasks count]);
iter = [tasks objectEnumerator];
i = 0;
PtyTaskDebugLog(@"Iterating over %lu tasks\n", (unsigned long)_tasks.count);
iter = [_tasks objectEnumerator];
BOOL notifyOfCoprocessChange = NO;
 
PTYTask *task;
while ((task = [iter nextObject])) {
PtyTaskDebugLog(@"Got task %d\n", i);
PtyTaskDebugLog(@"Got task %@\n", task);
int fd = [task fd];
 
if (fd >= 0) {
Loading
Loading
@@ -375,13 +387,13 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
[handledFds addObject:@(fd)];
 
if ([self handleReadOnFileDescriptor:fd task:task fdSet:&rfds]) {
iter = [tasks objectEnumerator];
iter = [_tasks objectEnumerator];
}
if ([self handleWriteOnFileDescriptor:fd task:task fdSet:&wfds]) {
iter = [tasks objectEnumerator];
iter = [_tasks objectEnumerator];
}
if ([self handleErrorOnFileDescriptor:fd task:task fdSet:&efds]) {
iter = [tasks objectEnumerator];
iter = [_tasks objectEnumerator];
}
// Move input around between coprocess and main process.
if ([task fd] >= 0 && ![task hasBrokenPipe]) { // Make sure the pipe wasn't just broken.
Loading
Loading
@@ -418,9 +430,44 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
}
[task release];
}
++i;
PtyTaskDebugLog(@"About to get task %d\n", i);
}
// Handle coprocess reads, writes, and deaths for coprocess-only tasks.
PtyTaskDebugLog(@"Iterating over %lu coprocess-only tasks\n", (unsigned long)_coprocessOnlyTasks.count);
for (PTYTask *coprocessOnlyTask in _coprocessOnlyTasks) {
@synchronized (coprocessOnlyTask) {
PtyTaskDebugLog(@"Checking coprocess olnly task %@", coprocessOnlyTask);
Coprocess *coprocess = [coprocessOnlyTask coprocess];
if (coprocess) {
int fd = [coprocess readFileDescriptor];
if ([handledFds containsObject:@(fd)]) {
NSLog(@"Duplicate fd %d", fd);
continue;
}
[handledFds addObject:@(fd)];
[self handleReadOnFileDescriptor:fd task:coprocessOnlyTask withCoprocess:coprocess fdSet:&rfds];
[self handleErrorOnFileDescriptor:fd withCoprocess:coprocess fdSet:&efds];
// Handle writes
int coprocessWriteFd = [coprocess writeFileDescriptor];
if ([handledFds containsObject:@(coprocessWriteFd)]) {
NSLog(@"Duplicate fd %d", coprocessWriteFd);
continue;
}
[handledFds addObject:@(coprocessWriteFd)];
[self handleWriteOnFileDescriptor:coprocessWriteFd withCoprocess:coprocess fdSet:&wfds];
if ([coprocess eof]) {
[deadpool addObject:@([coprocess pid])];
[coprocess terminate];
[coprocessOnlyTask setCoprocess:nil];
notifyOfCoprocessChange = YES;
}
}
}
}
PtyTaskDebugLog(@"run3: unlock");
[tasksLock unlock];
if (notifyOfCoprocessChange) {
Loading
Loading
@@ -444,4 +491,12 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
object:nil];
}
 
- (void)lock {
[tasksLock lock];
}
- (void)unlock {
[tasksLock unlock];
}
@end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment