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

Add Poll helper and debugging code to track down bug where poll() doesn't return on EOF sometimes.

parent 5efbe634
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -4,7 +4,7 @@
// Use this instead to debug this module:
// #define PtyTaskDebugLog NSLog
 
#define MAXRW 1024
#define MAXRW 1
 
#import "PTYTask.h"
#import "Coprocess.h"
Loading
Loading
@@ -23,6 +23,8 @@
#include <sys/user.h>
#include <unistd.h>
#include <util.h>
#import "DebugLogging.h"
#import "iTermPollHelper.h"
 
#define CTRLKEY(c) ((c)-'A'+1)
 
Loading
Loading
@@ -175,8 +177,13 @@ static void reapchild(int n)
path = [progpath copy];
 
setup_tty_param(&term, &win, width, height, isUTF8);
// Register a handler for the child death signal.
signal(SIGCHLD, reapchild);
// Block SIGCHLD
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGCHLD);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
const char* argpath;
argpath = [[progpath stringByStandardizingPath] UTF8String];
 
Loading
Loading
@@ -281,6 +288,12 @@ static void reapchild(int n)
for (int i = 0; i < iterations; ++i) {
// Only read up to MAXRW*iterations bytes, then release control
ssize_t n = read(fd, buffer + bytesRead, MAXRW);
LOG(@"read returns %d", (int)n);
int theError = errno;
if (n == 0) {
[self brokenPipe];
return;
}
if (n < 0) {
// There was a read error.
if (errno != EAGAIN && errno != EINTR) {
Loading
Loading
@@ -305,6 +318,7 @@ static void reapchild(int n)
 
hasOutput = YES;
 
// NSLog(@"%.*s", bytesRead, buffer);
// Send data to the terminal
[self readTask:buffer length:bytesRead];
}
Loading
Loading
@@ -381,6 +395,7 @@ static void reapchild(int n)
 
- (void)brokenPipe
{
NSLog(@"* BROKEN PIPE *");
brokenPipe_ = YES;
[[TaskNotifier sharedInstance] deregisterTask:self];
[(NSObject *)self.delegate performSelectorOnMainThread:@selector(brokenPipe)
Loading
Loading
@@ -447,6 +462,7 @@ static void reapchild(int n)
// being passed a half-broken fd. We must change it because after this
// function returns, a new task may be created with this fd and then
// the select thread wouldn't know which task a fd belongs to.
DLog(@"Stop called from %@", [NSThread callStackSymbols]);
fd = -1;
}
}
Loading
Loading
Loading
Loading
@@ -10,8 +10,9 @@
#import "Coprocess.h"
#import "DebugLogging.h"
#import "PTYTask.h"
#import "iTermPollHelper.h"
 
#define PtyTaskDebugLog(args...)
#define PtyTaskDebugLog DLog
 
NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
 
Loading
Loading
@@ -129,17 +130,13 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
NSEnumerator* iter;
PTYTask* task;
NSAutoreleasePool* autoreleasePool = [[NSAutoreleasePool alloc] init];
iTermPollHelper *pollHelper = [[iTermPollHelper alloc] init];
// FIXME: replace this with something better...
for(;;) {
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
while (1) {
[pollHelper reset];
// Unblock pipe to interrupt select() whenever a PTYTask register/unregisters
highfd = unblockPipeR;
FD_SET(unblockPipeR, &rfds);
[pollHelper addFileDescriptor:unblockPipeR forReading:YES writing:NO identifier:nil];
NSMutableSet* handledFds = [[NSMutableSet alloc] initWithCapacity:[tasks count]];
// Add all the PTYTask pipes
Loading
Loading
@@ -184,48 +181,26 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
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);
FD_SET(fd, &efds);
PtyTaskDebugLog(@"Task has fd of %d\n", fd);
if (fd >= 0) {
[pollHelper addFileDescriptor:fd
forReading:[task wantsRead]
writing:[task wantsWrite]
identifier:task];
}
@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);
}
if ([coprocess wantToWrite]) {
int wfd = [coprocess writeFileDescriptor];
if (wfd > highfd) {
highfd = wfd;
}
FD_SET(wfd, &wfds);
}
if (![coprocess eof]) {
int rfd = [coprocess readFileDescriptor];
if (rfd > highfd) {
highfd = rfd;
}
FD_SET(rfd, &efds);
int wfd = [coprocess writeFileDescriptor];
if (wfd > highfd) {
highfd = wfd;
}
FD_SET(wfd, &efds);
}
BOOL reading = ([coprocess wantToRead] && [task writeBufferHasRoom]) || ![coprocess eof];
[pollHelper addFileDescriptor:[coprocess readFileDescriptor]
forReading:reading
writing:NO
identifier:coprocess];
[pollHelper addFileDescriptor:[coprocess writeFileDescriptor]
forReading:NO
writing:[coprocess wantToWrite]
identifier:coprocess];
}
}
++i;
Loading
Loading
@@ -237,18 +212,13 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
[[NSNotificationCenter defaultCenter] postNotificationName:kTaskNotifierDidSpin object:nil];
 
// Poll...
if (select(highfd+1, &rfds, &wfds, &efds, NULL) <= 0) {
switch(errno) {
case EAGAIN:
case EINTR:
default:
goto breakloop;
// If the file descriptor is closed in the main thread there's a race where sometimes you'll get an EBADF.
}
}
DLog(@"call poll...");
[pollHelper poll];
DLog(@"continuing after poll");
// Interrupted?
if (FD_ISSET(unblockPipeR, &rfds)) {
if ([pollHelper flagsForFd:unblockPipeR] & kiTermPollHelperFlagReadable) {
DLog(@"Interrupted");
char dummy[32];
do {
read(unblockPipeR, dummy, sizeof(dummy));
Loading
Loading
@@ -264,8 +234,8 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
BOOL notifyOfCoprocessChange = NO;
while ((task = [iter nextObject])) {
PtyTaskDebugLog(@"Got task %d\n", i);
int fd = [task fd];
PtyTaskDebugLog(@"Got task %d, fd=%d\n", i, fd);
if (fd >= 0) {
// This is mostly paranoia, but if two threads
// end up with the same fd (because one closed
Loading
Loading
@@ -279,7 +249,8 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
[task retain];
[handledFds addObject:[NSNumber numberWithInt:fd]];
if (FD_ISSET(fd, &rfds)) {
NSUInteger taskFlags = [pollHelper flagsForFd:fd];
if (taskFlags & kiTermPollHelperFlagReadable) {
PtyTaskDebugLog(@"run/processRead: unlock");
[tasksLock unlock];
[task processRead];
Loading
Loading
@@ -291,7 +262,7 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
iter = [tasks objectEnumerator];
}
}
if (FD_ISSET(fd, &wfds)) {
if (taskFlags & kiTermPollHelperFlagWritable) {
PtyTaskDebugLog(@"run/processWrite: unlock");
[tasksLock unlock];
[task processWrite];
Loading
Loading
@@ -303,20 +274,6 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
iter = [tasks objectEnumerator];
}
}
if (FD_ISSET(fd, &efds)) {
PtyTaskDebugLog(@"run/brokenPipe: unlock");
[tasksLock unlock];
// brokenPipe will call deregisterTask and add the pid to
// deadpool.
[task brokenPipe];
PtyTaskDebugLog(@"run/brokenPipe: lock");
[tasksLock lock];
if (tasksChanged) {
PtyTaskDebugLog(@"Restart iteration\n");
tasksChanged = NO;
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
@@ -329,7 +286,8 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
continue;
}
[handledFds addObject:[NSNumber numberWithInt:fd]];
if (![coprocess eof] && FD_ISSET(fd, &rfds)) {
NSUInteger coprocessFlags = [pollHelper flagsForFd:fd];
if (![coprocess eof] && (coprocessFlags & kiTermPollHelperFlagReadable)) {
[coprocess read];
[task writeTask:coprocess.inputBuffer];
[coprocess.inputBuffer setLength:0];
Loading
Loading
@@ -339,15 +297,13 @@ NSString *const kTaskNotifierDidSpin = @"kTaskNotifierDidSpin";
}
fd = [coprocess writeFileDescriptor];
coprocessFlags = [pollHelper flagsForFd:fd];
if ([handledFds containsObject:[NSNumber numberWithInt:fd]]) {
NSLog(@"Duplicate fd %d", fd);
continue;
}
[handledFds addObject:[NSNumber numberWithInt:fd]];
if (FD_ISSET(fd, &efds)) {
coprocess.eof = YES;
}
if (FD_ISSET(fd, &wfds)) {
if (coprocessFlags & kiTermPollHelperFlagWritable) {
if (![coprocess eof]) {
[coprocess write];
}
Loading
Loading
Loading
Loading
@@ -747,6 +747,9 @@
A6A5991D1887C63700CB4209 /* ToolCommandHistoryView.h in Headers */ = {isa = PBXBuildFile; fileRef = A6A5991B1887C63700CB4209 /* ToolCommandHistoryView.h */; };
A6A5991E1887C63700CB4209 /* ToolCommandHistoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = A6A5991C1887C63700CB4209 /* ToolCommandHistoryView.m */; };
A6A5991F1887C63700CB4209 /* ToolCommandHistoryView.m in Sources */ = {isa = PBXBuildFile; fileRef = A6A5991C1887C63700CB4209 /* ToolCommandHistoryView.m */; };
A6B5133018E3CD2900D249A5 /* iTermPollHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = A6B5132E18E3CD2900D249A5 /* iTermPollHelper.h */; };
A6B5133118E3CD2900D249A5 /* iTermPollHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B5132F18E3CD2900D249A5 /* iTermPollHelper.m */; };
A6B5133218E3CD2900D249A5 /* iTermPollHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = A6B5132F18E3CD2900D249A5 /* iTermPollHelper.m */; };
A6C4E8E01846E13800CFAA77 /* IntervalTree.m in Sources */ = {isa = PBXBuildFile; fileRef = A6C4E8DC1846E13800CFAA77 /* IntervalTree.m */; };
A6C4E8E11846E13800CFAA77 /* IntervalTree.m in Sources */ = {isa = PBXBuildFile; fileRef = A6C4E8DC1846E13800CFAA77 /* IntervalTree.m */; };
A6C4E8E21846E13800CFAA77 /* IntervalTree.h in Headers */ = {isa = PBXBuildFile; fileRef = A6C4E8DD1846E13800CFAA77 /* IntervalTree.h */; };
Loading
Loading
@@ -1291,6 +1294,8 @@
A6A13ABA18C34F6400B241ED /* VT100XtermParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VT100XtermParser.m; sourceTree = "<group>"; };
A6A5991B1887C63700CB4209 /* ToolCommandHistoryView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ToolCommandHistoryView.h; sourceTree = "<group>"; };
A6A5991C1887C63700CB4209 /* ToolCommandHistoryView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ToolCommandHistoryView.m; sourceTree = "<group>"; };
A6B5132E18E3CD2900D249A5 /* iTermPollHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iTermPollHelper.h; sourceTree = "<group>"; };
A6B5132F18E3CD2900D249A5 /* iTermPollHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = iTermPollHelper.m; sourceTree = "<group>"; };
A6C4E8DC1846E13800CFAA77 /* IntervalTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = IntervalTree.m; sourceTree = "<group>"; };
A6C4E8DD1846E13800CFAA77 /* IntervalTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntervalTree.h; sourceTree = "<group>"; };
A6C4E8E61846E32600CFAA77 /* IntervalTreeTest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = IntervalTreeTest.m; path = iTermTests/IntervalTreeTest.m; sourceTree = "<group>"; };
Loading
Loading
@@ -1449,6 +1454,8 @@
0464AB0D006CD2EC7F000001 /* JTerminal */ = {
isa = PBXGroup;
children = (
A6B5132E18E3CD2900D249A5 /* iTermPollHelper.h */,
A6B5132F18E3CD2900D249A5 /* iTermPollHelper.m */,
0464AB0E006CD2EC7F000001 /* Classes */,
0464AB15006CD2EC7F000001 /* Headers */,
0464AB1F006CD2EC7F000001 /* Other Sources */,
Loading
Loading
@@ -2448,6 +2455,7 @@
1D9F6AB8140C9DC4009B5CD4 /* FutureMethods.h in Headers */,
1DE8DC891415513A00F83147 /* ToolbeltView.h in Headers */,
1DE8DC8D1415546000F83147 /* ToolProfiles.h in Headers */,
A6B5133018E3CD2900D249A5 /* iTermPollHelper.h in Headers */,
1DE8DDAD1415648600F83147 /* ToolPasteHistory.h in Headers */,
1DE8DF361415799700F83147 /* ToolWrapper.h in Headers */,
1D19C71414171F1D00617E08 /* ToolJobs.h in Headers */,
Loading
Loading
@@ -2809,6 +2817,7 @@
A647E39C18C3515900450FA1 /* VT100AnsiParser.m in Sources */,
A68A3111186E2EDA007F550F /* PopupEntry.m in Sources */,
1D9A552D180FA69900B42CE9 /* SearchResult.m in Sources */,
A6B5133218E3CD2900D249A5 /* iTermPollHelper.m in Sources */,
A63F409C183B3AA7003A6A6D /* PTYNoteView.m in Sources */,
1D9A55AB180FA8B700B42CE9 /* PSMProgressIndicator.m in Sources */,
1D9A5593180FA87000B42CE9 /* PTYFontInfo.m in Sources */,
Loading
Loading
@@ -3127,6 +3136,7 @@
1D988598135D23BD0072023F /* ProcessCache.m in Sources */,
A68A30DC186D1429007F550F /* SCPFile.m in Sources */,
1D624BC81386E09E00111319 /* GTMCarbonEvent.m in Sources */,
A6B5133118E3CD2900D249A5 /* iTermPollHelper.m in Sources */,
1DA8117F13CEA30A00CCA89A /* FontSizeEstimator.m in Sources */,
1D81F0C1183C3C2D00910838 /* NSView+RecursiveDescription.m in Sources */,
1DCA5ED013EE507800B7725E /* WindowArrangements.m in Sources */,
Loading
Loading
//
// iTermPollHelper.h
// iTerm
//
// Created by George Nachman on 3/26/14.
//
//
#import <Foundation/Foundation.h>
#define kiTermPollHelperFlagReadable (1 << 0)
#define kiTermPollHelperFlagWritable (1 << 1)
extern NSMutableString *gLog;
#define LOG(args...) \
do { \
[gLog appendFormat:args]; \
[gLog appendString:@"\n"]; \
} while (0);
@interface iTermPollHelper : NSObject
- (void)reset;
- (void)addFileDescriptor:(int)fd
forReading:(BOOL)reading
writing:(BOOL)writing
identifier:(NSObject *)identifier;
- (void)poll;
- (NSUInteger)flagsForFd:(int)fd;
@end
//
// iTermPollHelper.m
// iTerm
//
// Created by George Nachman on 3/26/14.
//
//
#import "iTermPollHelper.h"
#include <poll.h>
char gBuffer[100];
@interface CStructMutableArray : NSObject
@property(nonatomic, readonly) int count;
- (id)initWithStructSize:(size_t)structSize;
- (void)addObjectWithBlock:(void (^)(void *ptr))assignBlock;
- (void *)pointerToObjectAtIndex:(NSInteger)index;
@end
@implementation CStructMutableArray {
char *_storage;
size_t _structSize;
int _count;
int _capacity;
}
- (id)initWithStructSize:(size_t)structSize {
self = [super init];
if (self) {
_capacity = 1;
_structSize = structSize;
_storage = calloc(_capacity, _structSize);
}
return self;
}
- (int)count {
return _count;
}
- (void)addObjectWithBlock:(void (^)(void *ptr))assignBlock {
if (_capacity == _count) {
int originalSizeInBytes = _capacity * _structSize;
_capacity *= 2;
_storage = realloc(_storage, _capacity * _structSize);
memset(_storage + originalSizeInBytes, 0, originalSizeInBytes);
}
int index = _count;
++_count;
assignBlock(_storage + (_structSize * index));
}
- (void *)pointerToObjectAtIndex:(NSInteger)index {
assert(index >= 0 && index < _count);
return _storage + (_structSize * index);
}
@end
@implementation iTermPollHelper {
CStructMutableArray *_fds;
NSMutableArray *_identifiers; // parallel to fds
NSMutableDictionary *_index; // file descriptor -> index
}
NSMutableString *gLog;
- (id)init {
gLog = [[NSMutableString alloc] init];
self = [super init];
if (self) {
_fds = [[CStructMutableArray alloc] initWithStructSize:sizeof(struct pollfd)];
_identifiers = [[NSMutableArray alloc] init];
_index = [[NSMutableDictionary alloc] init];
}
return self;
}
- (void)reset {
LOG(@"Reset");
[_fds release];
_fds = [[CStructMutableArray alloc] initWithStructSize:sizeof(struct pollfd)];
[_identifiers removeAllObjects];
[_index removeAllObjects];
}
- (void)addFileDescriptor:(int)fd
forReading:(BOOL)reading
writing:(BOOL)writing
identifier:(NSObject *)identifier {
LOG(@"Poll on %d", fd);
_index[@(fd)] = @(_fds.count);
[_identifiers addObject:identifier ?: [NSNull null]];
[_fds addObjectWithBlock:^(void *ptr) {
struct pollfd *pollfd = (struct pollfd *)ptr;
pollfd->fd = fd;
pollfd->events = ((reading ? (POLLIN) : 0) | (writing ? POLLOUT : 0));
pollfd->revents = 0;
}];
}
void TryReadingFromFd(int fd) {
char buffer[1];
int n = read(fd, buffer, 1);
NSLog(@"Read returns %d, errno=%d", n, errno);
}
- (void)poll {
struct pollfd *pollfds = [_fds pointerToObjectAtIndex:0];
int count = _fds.count;
int numDescriptors;
do {
LOG(@"Start poll");
numDescriptors = poll(pollfds, count, -1);
LOG(@"Return from poll");
assert(numDescriptors != 0);
if (numDescriptors < 0) {
assert(errno == EAGAIN || errno == EINTR);
}
} while (numDescriptors < 0);
}
- (NSUInteger)flagsForFd:(int)fd {
NSNumber *n = _index[@(fd)];
if (!n) {
return 0;
}
int index = [n intValue];
struct pollfd *pollfd = [_fds pointerToObjectAtIndex:index];
NSUInteger flags = 0;
assert(!(pollfd->revents & POLLNVAL));
if (pollfd->revents & (POLLIN | POLLERR | POLLHUP)) {
LOG(@"fd %d is readable", fd);
flags |= kiTermPollHelperFlagReadable;
}
if (pollfd->revents & POLLOUT) {
LOG(@"fd %d is writable", fd);
flags |= kiTermPollHelperFlagWritable;
}
return flags;
}
@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