From: Michael D. Lowis Date: Fri, 14 Jul 2017 16:00:33 +0000 (-0400) Subject: Fixed some bugs in the asynchronous command handling logic X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=0a7d0ba0532f3a34a51bcae07bb33c671d09f4da;p=projs%2Ftide.git Fixed some bugs in the asynchronous command handling logic --- diff --git a/TODO.md b/TODO.md index b4be0d7..b5c6e91 100644 --- a/TODO.md +++ b/TODO.md @@ -46,6 +46,7 @@ Possible Shortcuts: Maybe think about addressing these later: +* switch buf.c to using a mmap buffer of utf-8/binary data * add current dir to path * add support for guidefiles * Shift+Insert should insert primary selection diff --git a/lib/buf.c b/lib/buf.c index 782b696..5c8bfb3 100644 --- a/lib/buf.c +++ b/lib/buf.c @@ -200,9 +200,12 @@ size_t buf_change(Buf* buf, size_t beg, size_t end) { void buf_chomp(Buf* buf) { size_t end = buf_end(buf); if (!end) return; - delete(buf, end-1); - if (buf->undo->insert && buf->undo->data.ins.end == end) - buf->undo->data.ins.end--; + Rune r = buf_get(buf, end-1); + if (r == RUNE_CRLF || r == '\n') { + delete(buf, end-1); + if (buf->undo->insert && buf->undo->data.ins.end == end) + buf->undo->data.ins.end--; + } } void buf_undo(Buf* buf, Sel* sel) { diff --git a/lib/event.c b/lib/event.c index ad6d8e7..284f3ae 100644 --- a/lib/event.c +++ b/lib/event.c @@ -35,14 +35,15 @@ bool event_poll(int ms) { /* if the desriptor is done or errored, throw it out */ if (Descriptors[i].revents & (POLLNVAL|POLLERR|POLLHUP)) { close(Descriptors[i].fd); - Descriptors[i].fd = -1; + Descriptors[i].fd = -Descriptors[i].fd; + EventData[i].fn(Descriptors[i].fd, EventData[i].data); } } /* remove any closed or invalid descriptors */ size_t nfds = 0; for (int i = 0; i < NumDescriptors; i++) { - if (Descriptors[i].fd != -1) { + if (Descriptors[i].fd >= 0) { Descriptors[nfds] = Descriptors[i]; EventData[nfds++] = EventData[i]; } diff --git a/lib/exec.c b/lib/exec.c index ef4264d..c264ea4 100644 --- a/lib/exec.c +++ b/lib/exec.c @@ -19,6 +19,7 @@ typedef struct { typedef struct Job Job; typedef struct { + Job* job; /* pointer to the job teh receiver belongs to */ View* view; /* destination view */ size_t beg; /* start of output */ size_t count; /* number of bytes written */ @@ -27,7 +28,7 @@ typedef struct { struct Job { Job* next; /* Pointer to previous job in the job list */ Job* prev; /* Pointer to next job in the job list */ - int pid; /* process id of the running job */ + Proc proc; /* Process id and descriptors */ size_t ndata; /* number of bytes to write to stdout */ size_t nwrite; /* number of bytes written to stdout so far */ char* data; /* data to write to stdout */ @@ -38,43 +39,41 @@ struct Job { static Job* JobList = NULL; +static void job_closefd(Job* job, int fd); +static bool job_done(Job* job); +static Job* job_finish(Job* job); static void send_data(int fd, void* data); static void recv_data(int fd, void* data); -static void watch_or_close(bool valid, int dir, int fd, void* data); +static int watch_or_close(bool valid, int dir, int fd, void* data); static void rcvr_finish(Rcvr* rcvr); static int execute(char** cmd, Proc* proc); bool exec_reap(void) { - int pid; - while ((pid = waitpid(-1, NULL, WNOHANG)) > 0) { - Job* job = JobList; - for (; job && job->pid != pid; job = job->next); - if (job && job->pid == pid) { + Job* job = JobList; + while (job) { + if (job_done(job)) { rcvr_finish(&(job->out_rcvr)); rcvr_finish(&(job->err_rcvr)); - if (job->prev) { - job->prev->next = job->next; - job->next->prev = job->prev; - } else { - for (; job && job->prev; job = job->prev); - JobList = (JobList == job ? NULL : job); - } - free(job); + job = job_finish(job); + } else { + job = job->next; } } + while (waitpid(-1, NULL, WNOHANG) > 0); return (JobList != NULL); } void exec_job(char** cmd, char* data, size_t ndata, View* dest) { - Proc proc; Job* job = calloc(1, sizeof(Job)); - job->pid = execute(cmd, &proc); - if (job->pid < 0) { + job->proc.pid = execute(cmd, &(job->proc)); + if (job->proc.pid < 0) { die("job_start() :"); } else { /* add the job to the job list */ job->err_rcvr.view = win_view(TAGS); + job->err_rcvr.job = job; job->out_rcvr.view = dest; + job->out_rcvr.job = job; job->ndata = ndata; job->nwrite = 0; job->data = data; @@ -85,9 +84,9 @@ void exec_job(char** cmd, char* data, size_t ndata, View* dest) { bool need_in = (job->data != NULL), need_out = (dest != NULL), need_err = (need_in || need_out); - watch_or_close(need_in, OUTPUT, proc.in, job); - watch_or_close(need_out, INPUT, proc.out, &(job->out_rcvr)); - watch_or_close(need_err, INPUT, proc.err, &(job->err_rcvr)); + job->proc.in = watch_or_close(need_in, OUTPUT, job->proc.in, job); + job->proc.out = watch_or_close(need_out, INPUT, job->proc.out, &(job->out_rcvr)); + job->proc.err = watch_or_close(need_err, INPUT, job->proc.err, &(job->err_rcvr)); } } @@ -124,50 +123,87 @@ int exec_spawn(char** cmd, int* in, int* out) { return proc.pid; } +static void job_closefd(Job* job, int fd) { + if (fd >= 0) close(fd), fd = -fd; + if (job->proc.in == -fd) job->proc.in = fd; + if (job->proc.out == -fd) job->proc.out = fd; + if (job->proc.err == -fd) job->proc.err = fd; +} + +static bool job_done(Job* job) { + return ((job->proc.in < 0) && (job->proc.out < 0) && (job->proc.err < 0)); +} + +static Job* job_finish(Job* job) { + Job* next = job->next; + if (job->prev) { + job->prev->next = job->next; + job->next->prev = job->prev; + } else { + JobList = job->next; + } + free(job->data); + free(job); + return next; +} + static void send_data(int fd, void* data) { Job* job = data; - long nwrite = write(fd, (job->data + job->nwrite), job->ndata); - if (nwrite < 0) { free(job->data); close(fd); return; } - job->ndata -= nwrite; - job->nwrite += nwrite; - if (job->ndata <= 0) { free(job->data); close(fd); return; } + if (fd >= 0) { + long nwrite = write(fd, (job->data + job->nwrite), job->ndata); + if (nwrite >= 0) { + job->ndata -= nwrite; + job->nwrite += nwrite; + } + if (nwrite < 0 || job->ndata <= 0) + job_closefd(job, fd); + } else { + job_closefd(job, fd); + } } static void recv_data(int fd, void* data) { static char buffer[4096]; - long i = 0, nread = read(fd, buffer, sizeof(buffer)); Rcvr* rcvr = data; + Job* job = rcvr->job; View* view = rcvr->view; Buf* buf = &(rcvr->view->buffer); Sel sel = view->selection; - if (nread > 0) { - if (!rcvr->count) { - if (sel.end < sel.beg) { - size_t temp = sel.beg; - sel.beg = sel.end, sel.end = temp; + + if (fd >= 0) { + long i = 0, nread = read(fd, buffer, sizeof(buffer)); + if (nread > 0) { + if (!rcvr->count) { + if (sel.end < sel.beg) { + size_t temp = sel.beg; + sel.beg = sel.end, sel.end = temp; + } + rcvr->beg = sel.beg = sel.end = buf_change(buf, sel.beg, sel.end); + view->selection = sel; } - rcvr->beg = sel.beg = sel.end = buf_change(buf, sel.beg, sel.end); - view->selection = sel; - buf_loglock(buf); - } - for (; i < nread;) { - Rune r; - size_t len = 0; - while (!utf8decode(&r, &len, buffer[i++])); - view_insert(rcvr->view, false, r); - rcvr->count++; + for (; i < nread;) { + Rune r; + size_t len = 0; + while (!utf8decode(&r, &len, buffer[i++])); + view_insert(rcvr->view, false, r); + rcvr->count++; + } + } else { + close(fd); + job_closefd(job, -fd); } } else { - close(fd); + job_closefd(job, fd); } } -static void watch_or_close(bool valid, int dir, int fd, void* data) { +static int watch_or_close(bool valid, int dir, int fd, void* data) { event_cbfn_t fn = (dir == OUTPUT ? send_data : recv_data); if (valid) event_watchfd(fd, dir, fn, data); else - close(fd); + close(fd), fd = -fd; + return fd; } static void rcvr_finish(Rcvr* rcvr) { diff --git a/lib/win.c b/lib/win.c index 73dd399..df1a6d6 100644 --- a/lib/win.c +++ b/lib/win.c @@ -73,6 +73,7 @@ void win_loop(void) { event_watchfd(x11_connfd(), INPUT, win_update, NULL); while (x11_running()) { bool pending = event_poll(ms); + exec_reap(); int nevents = x11_events_queued(); if (update_focus() || pending || nevents) { x11_events_take(); @@ -80,7 +81,6 @@ void win_loop(void) { x11_flip(); } x11_flush(); - exec_reap(); } x11_finish(); }