From 58499dda23b3f3f7e805187cd553e83652e42f66 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Wed, 6 Jul 2016 17:15:13 +0000 Subject: [PATCH] use gcd to read child data and monitor if child is still alive. git-svn-id: svn://qnap.local/TwoTerm/branches/gcd_dispatch@3105 5590a31f-7b70-45f8-8c82-aa3a8e5f4507 --- TermWindowController.h | 6 ++ TermWindowController.mm | 143 ++++++++++++++++++++++++++++++++-------- Views/EmulatorView.h | 7 +- Views/EmulatorView.mm | 75 ++++++++++++++++++++- 4 files changed, 198 insertions(+), 33 deletions(-) diff --git a/TermWindowController.h b/TermWindowController.h index 0899076..640095c 100644 --- a/TermWindowController.h +++ b/TermWindowController.h @@ -30,6 +30,12 @@ int _child; + int _fd; + pid_t _pid; + + dispatch_source_t _read_source; + dispatch_source_t _wait_source; + } @property (nonatomic, retain) NSDictionary *parameters; diff --git a/TermWindowController.mm b/TermWindowController.mm index ab0aabd..de14739 100644 --- a/TermWindowController.mm +++ b/TermWindowController.mm @@ -70,11 +70,8 @@ -(void)initPTY { - int pid; - int fd; struct termios term; struct winsize ws = [_emulator defaultSize]; - int flags; memset(&term, 0, sizeof(term)); @@ -95,16 +92,16 @@ [_emulator initTerm: &term]; - pid = forkpty(&fd, NULL, &term, &ws); + _pid = forkpty(&_fd, NULL, &term, &ws); - if (pid < 0) + if (_pid < 0) { fprintf(stderr, "forkpty failed\n"); fflush(stderr); return; } - if (pid == 0) + if (_pid == 0) { std::vector environ; @@ -153,19 +150,16 @@ // child } - - // non-blocking io. - if (fcntl(fd, F_GETFL, &flags) < 0) flags = 0; - fcntl(fd, F_SETFL, flags | O_NONBLOCK); - + [_emulatorView resizeTo: iSize(ws.ws_col, ws.ws_row) animated: NO]; - + + NSWindow *window = [self window]; + if (![_emulator resizable]) { - NSWindow *window = [self window]; NSUInteger mask = [window styleMask]; @@ -173,24 +167,108 @@ [window setStyleMask: mask & ~NSResizableWindowMask]; } - - + + [window setMinSize: [window frame].size]; + + [_emulatorView setFd: _fd]; + [self monitor]; + + /* if (!_childMonitor) { _childMonitor = [ChildMonitor new]; [_childMonitor setDelegate: _emulatorView]; } - - [_childMonitor setChildPID: pid]; - [_childMonitor setFd: fd]; - - _child = pid; - - [_emulatorView setFd: fd]; - //[_emulatorView startBackgroundReader]; - [_childMonitor start]; + */ } +-(void)monitor { + + int fd = _fd; + int pid = _pid; + + int flags; + // non-blocking io. + if (fcntl(_fd, F_GETFL, &flags) < 0) flags = 0; + fcntl(_fd, F_SETFL, flags | O_NONBLOCK); + + + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + + _wait_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, + pid, DISPATCH_PROC_EXIT, queue); + if (_wait_source) + { + + dispatch_source_set_event_handler(_wait_source, ^{ + + int status = 0; + int ok = waitpid(pid, &status, WNOHANG); + + _pid = 0; + //dispatch_async(dispatch_get_main_queue(), ^(){ + [_emulatorView childFinished: status]; + //}); + + dispatch_source_cancel(_wait_source); + dispatch_release(_wait_source); + _wait_source = nullptr; + }); + + dispatch_resume(_wait_source); + } + + + _read_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, + fd, 0, queue); + if (_read_source) + { + // Install the event handler + dispatch_source_set_event_handler(_read_source, ^{ + + static uint8_t sbuffer[1024]; + size_t estimated = dispatch_source_get_data(_read_source) + 1; + + + uint8_t *buffer = estimated > sizeof(sbuffer) ? (uint8_t *)malloc(estimated) : sbuffer; + if (buffer) + { + ssize_t actual; + + for (;;) { + actual = read(fd, buffer, (estimated)); + if (actual < 0) { + if (errno == EINTR || errno == EAGAIN) return; + NSLog(@"read: %s", strerror(errno)); + dispatch_source_cancel(_read_source); + dispatch_release(_read_source); + _read_source = nullptr; + } + break; + } + + if (actual > 0) [_emulatorView processData: buffer size: actual]; + + if (buffer != sbuffer) free(buffer); + + if (actual == 0 && _pid == 0) { + dispatch_source_cancel(_read_source); + dispatch_release(_read_source); + _read_source = nullptr; + } + } + }); + + + dispatch_source_set_cancel_handler(_read_source, ^{ + _fd = -1; + [_emulatorView setFd: -1]; + close(fd); + }); + + dispatch_resume(_read_source); + } +} #pragma mark - #pragma mark NSWindowDelegate @@ -305,15 +383,26 @@ [self initPTY]; - [window setMinSize: [window frame].size]; } -(void)windowWillClose:(NSNotification *)notification { - [_childMonitor setDelegate: nil]; - [_childMonitor cancel]; + if (_wait_source) { + dispatch_source_cancel(_wait_source); + dispatch_release(_wait_source); + } + if (_read_source) { + dispatch_source_cancel(_read_source); + dispatch_release(_read_source); + } + + if (_pid) kill(_pid, 9); + [self autorelease]; } @end + + + diff --git a/Views/EmulatorView.h b/Views/EmulatorView.h index 2942d4b..24369f3 100644 --- a/Views/EmulatorView.h +++ b/Views/EmulatorView.h @@ -80,7 +80,7 @@ private: } @property (nonatomic, assign) BOOL scanLines; -@property (nonatomic, assign) int fd; +@property (atomic, assign) int fd; @property (nonatomic, assign) unsigned cursorType; @property (nonatomic, retain) NSColor *foregroundColor; @@ -103,12 +103,11 @@ private: -(IBAction)copy: (id)sender; +-(void)processData: (const uint8_t *)data size: (size_t)size; +-(void)childFinished: (int)status; @end -@interface EmulatorView (ChildMonitor) - -@end @interface EmulatorView (Cursor) diff --git a/Views/EmulatorView.mm b/Views/EmulatorView.mm index cfff312..3839d9d 100644 --- a/Views/EmulatorView.mm +++ b/Views/EmulatorView.mm @@ -448,10 +448,12 @@ -(void)keyDown:(NSEvent *)theEvent { //NSLog(@"keyDown:"); - + if (_fd < 0) return; + OutputChannel channel(_fd); iRect updateRect; // should be nil but whatever... + // todo -- after _fd closes, need to block further activity. [NSCursor setHiddenUntilMouseMoves: YES]; @@ -484,8 +486,76 @@ } +-(void)childFinished:(int)status { + + // called from other thread. + + NSLog(@"[process complete]"); + + dispatch_async(dispatch_get_main_queue(), ^(){ + + iRect updateRect; + + [self setCursorType: Screen::CursorTypeNone]; + //[self stopCursorTimer]; + //_screen.setCursorType(Screen::CursorTypeNone); + + _screen.beginUpdate(); + + _screen.setX(0); + _screen.incrementY(); + + for (const char *cp = "[Process completed]"; *cp; ++cp) + { + _screen.putc(*cp); + } + + + updateRect = _screen.endUpdate(); + + + [self invalidateIRect: updateRect]; + + //[_emulator writeLine: @"[Process completed]"]; + + }); + + + +} +-(void)processData:(const uint8_t *)buffer size:(size_t)size { + typedef void (*ProcessCharFX)(id, SEL, uint8_t, Screen *, OutputChannel *); + + ProcessCharFX fx; + SEL cmd; + OutputChannel channel(_fd); + iRect updateRect; + + cmd = @selector(processCharacter: screen: output:); + fx = (ProcessCharFX)[_emulator methodForSelector: cmd]; + + NSAutoreleasePool *pool; + pool = [NSAutoreleasePool new]; + _screen.beginUpdate(); + + + for (unsigned i = 0; i < size; ++i) + { + fx(_emulator,cmd, buffer[i], &_screen, &channel); + } + + updateRect = _screen.endUpdate(); + + dispatch_async(dispatch_get_main_queue(), ^(){ + + [self invalidateIRect: updateRect]; + + }); + + [pool release]; +} -(void)dataAvailable @@ -794,6 +864,7 @@ @end +#if 0 @implementation EmulatorView (ChildMonitor) -(void)childDataAvailable: (ChildMonitor *)monitor @@ -841,7 +912,7 @@ } @end - +#endif void ViewScreen::setSize(unsigned width, unsigned height)