diff options
| author | Quentin Carbonneaux | 2014-09-19 14:56:24 -0400 |
|---|---|---|
| committer | Quentin Carbonneaux | 2014-09-19 15:07:49 -0400 |
| commit | 371dd1e4f7cf8adb72cf5bbde40b89b0724740c7 (patch) | |
| tree | e22d1f416dcbbc11607ba54372ea8088413bbb98 | |
| parent | 3aa9b35a7fff2975558204b45ca3184c55ecff51 (diff) | |
superior resource management for async tasks
The previous implementation with reference counting on EBuf
was clunky. First, use of negative reference counts made the
logic unclear. Second, when an async task was started and a
file was reloaded in the window (eb_clr), some garbage could
be output to the new buffer at random positions. Third,
killing a buffer could leak file descriptors tied to long
running tasks.
The problems are solved by eagerly destructing all the
resources of tasks that output in a given buffer right
before its destruction.
| -rw-r--r-- | edit.c | 39 | ||||
| -rw-r--r-- | edit.h | 14 | ||||
| -rw-r--r-- | evnt.c | 48 | ||||
| -rw-r--r-- | evnt.h | 12 | ||||
| -rw-r--r-- | exec.c | 172 | ||||
| -rw-r--r-- | exec.h | 1 | ||||
| -rw-r--r-- | main.c | 7 | ||||
| -rw-r--r-- | win.c | 17 | ||||
| -rw-r--r-- | win.h | 2 |
9 files changed, 158 insertions, 154 deletions
@@ -180,7 +180,7 @@ log_clr(Log *l) } EBuf * -eb_new() +eb_new(int fd) { EBuf *eb; @@ -190,22 +190,18 @@ eb_new() eb->redo = log_new(); eb->ml = 0; eb->path = 0; - eb->refs = 0; + eb->tasks = 0; + if (fd != -1) + geteb(eb, fd); return eb; } -/* eb_kill - Free all the memory used by [eb]. If the - * refcount is non-zero, it switches its sign and does - * not free the memory used by [eb]. Otherwise, all - * the memory is freed. - */ void eb_kill(EBuf *eb) { Mark *m; - assert(eb->refs >= 0); - eb->refs = -eb->refs; + assert(!eb->tasks); buf_clr(&eb->b); log_clr(eb->undo); log_clr(eb->redo); @@ -217,30 +213,7 @@ eb_kill(EBuf *eb) free(eb->undo); free(eb->redo); free(eb->path); - if (eb->refs == 0) - free(eb); -} - -/* eb_clr - Reset the buffer contents and marks and read - * the new contents from the file descriptor [fd] if it is - * different from -1. - */ -void -eb_clr(EBuf *eb, int fd) -{ - Mark *m; - - buf_clr(&eb->b); - log_clr(eb->undo); - log_clr(eb->redo); - while (eb->ml) { - m = eb->ml->next; - free(eb->ml); - eb->ml = m; - } - if (fd != -1) - geteb(eb, fd); - assert(eb->path == 0); + free(eb); } void @@ -8,6 +8,7 @@ typedef struct log Log; typedef struct mark Mark; typedef struct ebuf EBuf; typedef struct ybuf YBuf; +typedef struct task Task; struct ybuf { Rune *r; @@ -17,19 +18,18 @@ struct ybuf { }; struct ebuf { - Buf b; /* base text buffer */ - Log *undo; /* undo redo logs */ + Buf b; /* base text buffer */ + Log *undo; /* undo redo logs */ Log *redo; - Mark *ml; /* buffer marks */ - char *path; /* file path */ + Mark *ml; /* buffer marks */ + char *path; /* file path */ time_t ftime; /* last mtime when written/read */ unsigned frev; /* last revision written */ - int refs; /* ref count, if <0 zombie buffer */ + Task *tasks; /* bound tasks currently running */ }; -EBuf *eb_new(void); +EBuf *eb_new(int); void eb_kill(EBuf *); -void eb_clr(EBuf *, int); unsigned eb_revision(EBuf *); void eb_del(EBuf *, unsigned, unsigned); void eb_ins(EBuf *, unsigned, Rune); @@ -15,7 +15,17 @@ int exiting; (a).tv_usec > (b).tv_usec : \ (a).tv_sec > (b).tv_sec) +typedef struct evnt Evnt; typedef struct alarm Alarm; + +struct evnt { + int valid; + int fd; + int flags; + void (*f)(int, int, void *); + void *p; +}; + struct alarm { struct timeval t; void (*f)(); @@ -58,27 +68,41 @@ ev_alarm(int ms, void (*f)()) } void -ev_register(Evnt e) +ev_register(int fd, int flags, void (*f)(int, int, void *), void *p) { elist = realloc(elist, (ne + 1) * sizeof(Evnt)); assert(elist); - elist[ne] = e; + elist[ne] = (Evnt){1, fd, flags, f, p}; ne++; } void +ev_cancel(int fd) +{ + int i; + + for (i=0; i<ne; i++) + if (elist[i].fd == fd) { + elist[i].valid = 0; + return; + } + assert(0); +} + +void ev_loop() { struct timeval tv; Alarm a; fd_set rfds, wfds; - int i, maxfd, flags; + int i, j, maxfd, flags; while (!exiting) { maxfd = -1; FD_ZERO(&rfds); FD_ZERO(&wfds); for (i=0; i < ne; i++) { + assert(elist[i].valid); if (elist[i].flags & ERead) FD_SET(elist[i].fd, &rfds); if (elist[i].flags & EWrite) @@ -111,27 +135,23 @@ ev_loop() popalarm(); a.f(); } - for (i=0; i < ne;) { + for (i=0; i<ne; i++) { flags = 0; if (FD_ISSET(elist[i].fd, &rfds)) flags |= ERead; if (FD_ISSET(elist[i].fd, &wfds)) flags |= EWrite; - if (flags == 0) { - i++; + if (flags == 0 || !elist[i].valid) continue; - } - if (elist[i].f(elist[i].fd, flags, elist[i].p)) { - ne--; - memmove(&elist[i], &elist[i+1], (ne - i) * sizeof(Evnt)); - continue; - } - i++; + elist[i].f(elist[i].fd, flags, elist[i].p); } + for (i=j=0; i<ne; i++) + if (elist[i].valid) + elist[j++] = elist[i]; + ne = j; } } - static void pushalarm(Alarm a) { @@ -1,5 +1,3 @@ -typedef struct evnt Evnt; - enum { MaxAlarms = 15, /* max number of concurrent alarms */ }; @@ -9,13 +7,7 @@ enum { EWrite = 2, }; -struct evnt { - int fd; - int flags; - int (*f)(int, int, void *); - void *p; -}; - int ev_alarm(int, void (*)(void)); -void ev_register(Evnt); +void ev_register(int, int, void (*)(int, int, void *), void *); +void ev_cancel(int); void ev_loop(void); @@ -29,6 +29,20 @@ struct ecmd { int (*f)(W *, EBuf *, unsigned); }; +struct task { + EBuf *eb; + unsigned p; /* insertion point in eb */ + unsigned ins; /* numbers of runes written in eb */ + char *ob; /* input to the command */ + unsigned no; /* number of bytes in the ob array */ + unsigned snt; /* number of bytes sent */ + char in[8]; /* input buffer for partial utf8 sequences XXX 8 */ + unsigned ni; /* numbers of bytes in the in array */ + int rfd; /* read fd, -1 if none */ + int wfd; /* write fd, -1 if none */ + Task *next; +}; + static ECmd *lookup(Buf *, unsigned, unsigned *); static unsigned skipb(Buf *, unsigned, int); static int get(W *, EBuf *, unsigned); @@ -129,9 +143,9 @@ ex_get(W *w, char *file) file1 = malloc(strlen(file)+1); assert(file1); strcpy(file1, file); - free(eb->path); - eb->path = 0; - eb_clr(eb, fd); + eb_kill(eb); + eb = eb_new(fd); + w->eb = eb; close(fd); stat(file1, &st); eb->path = file1; @@ -189,6 +203,29 @@ ex_put(EBuf *eb, char *file) return 0; } +/* ex_cancel - Cancel a running task and free all the resources + * related to it. + */ +void +ex_cancel(Task *t) +{ + Task **p; + + for (p=&t->eb->tasks; *p!=t; p=&(*p)->next) + assert(*p); + *p = t->next; + if (t->rfd >= 0) { + ev_cancel(t->rfd); + close(t->rfd); + } + if (t->wfd >= 0) { + ev_cancel(t->wfd); + close(t->wfd); + } + free(t->ob); + free(t); +} + /* static functions */ @@ -324,7 +361,7 @@ look(W *w, EBuf *eb, unsigned p0) static int new(W *w, EBuf *eb, unsigned p0) { - w = win_new(eb_new()); + w = win_new(); if (w) curwin = win_tag_toggle(w); else @@ -343,79 +380,52 @@ del(W *w, EBuf *eb, unsigned p0) return 0; } -typedef struct run Run; -struct run { - EBuf *eb; /* 0 if no more to read */ - unsigned p; /* insertion point in eb */ - unsigned ins; /* numbers of runes written in eb */ - char *ob; /* input to the command, 0 if none */ - unsigned no; /* number of bytes in the ob array */ - unsigned snt; /* number of bytes sent */ - char in[8]; /* input buffer for partial utf8 sequences XXX 8 */ - unsigned nin; /* numbers of bytes in the in array */ -}; - -static int +static void runev(int fd, int flag, void *data) { - Run *rn; + Task *t; int n, dec; unsigned char buf[2048], *p; Rune r; - rn = data; - n = rn->eb->refs; - if (n < 0) { - /* zombie buffer */ - rn->eb->refs++; -/* puts("tick"); */ - if (n == -1) -/* puts("tock!"), */ - free(rn->eb); - rn->eb = 0; - close(fd); - goto Reset; - } + t = data; if (flag & ERead) { - assert(rn->eb); - memcpy(buf, rn->in, rn->nin); - n = read(fd, &buf[rn->nin], sizeof buf - rn->nin); + assert(t->rfd == fd); + memcpy(buf, t->in, t->ni); + n = read(fd, &buf[t->ni], sizeof buf - t->ni); if (n <= 0) { - close(fd); - rn->eb->refs--; - rn->eb = 0; - goto Reset; + t->rfd = -1; + goto Close; } p = buf; while ((dec = utf8_decode_rune(&r, p, n))) { - eb_ins(rn->eb, rn->p + rn->ins++, r); + eb_ins(t->eb, t->p + t->ins++, r); p += dec; n -= dec; } - assert((unsigned)n <= sizeof rn->in); - rn->nin = n; - memcpy(rn->in, p, n); - eb_setmark(rn->eb, SelBeg, rn->p); - eb_setmark(rn->eb, SelEnd, rn->p + rn->ins); - eb_commit(rn->eb); + assert((unsigned)n <= sizeof t->in); + t->ni = n; + memcpy(t->in, p, n); + eb_setmark(t->eb, SelBeg, t->p); + eb_setmark(t->eb, SelEnd, t->p + t->ins); + eb_commit(t->eb); win_redraw_frame(); } if (flag & EWrite) { - assert(rn->ob); - n = write(fd, &rn->ob[rn->snt], rn->no - rn->snt); - rn->snt += n; - if (n < 0 || rn->snt == rn->no) { - close(fd); - free(rn->ob); - rn->ob = 0; - goto Reset; + assert(t->ob && t->wfd == fd); + n = write(fd, &t->ob[t->snt], t->no - t->snt); + t->snt += n; + if (n < 0 || t->snt == t->no) { + t->wfd = -1; + goto Close; } } - return 0; -Reset: - if (rn->eb == 0 && rn->ob == 0) - free(rn); - return 1; + return; +Close: + ev_cancel(fd); + close(fd); + if (t->rfd < 0 && t->wfd < 0) + ex_cancel(t); } static int @@ -424,7 +434,7 @@ run(W *w, EBuf *eb, unsigned p0) unsigned p1, eol, s0, s1; char *argv[4], *cmd, ctyp; int pin[2], pout[2]; - Run *r; + Task *t; eol = buf_eol(&eb->b, p0); p1 = 1 + skipb(&eb->b, eol-1, -1); @@ -459,46 +469,50 @@ run(W *w, EBuf *eb, unsigned p0) free(cmd); close(pin[0]); close(pout[1]); - r = calloc(1, sizeof *r); - assert(r); + t = calloc(1, sizeof *t); + assert(t); switch (ctyp) { case '>': - r->eb = eb; - r->p = eol+1; - r->ob = buftobytes(&w->eb->b, s0, s1, &r->no); + t->eb = eb; + t->p = eol+1; + t->ob = buftobytes(&w->eb->b, s0, s1, &t->no); break; case '<': - r->eb = w->eb; - r->p = s0; - r->ob = 0; + t->eb = w->eb; + t->p = s0; + t->ob = 0; eb_del(w->eb, s0, s1); break; case '|': - r->eb = w->eb; - r->p = s0; - r->ob = buftobytes(&w->eb->b, s0, s1, &r->no); + t->eb = w->eb; + t->p = s0; + t->ob = buftobytes(&w->eb->b, s0, s1, &t->no); eb_del(w->eb, s0, s1); break; case 0: - r->eb = eb; - r->p = eol+1; - r->ob = 0; + t->eb = eb; + t->p = eol+1; + t->ob = 0; break; default: abort(); } - r->eb->refs++; if (ctyp != 0 && ctyp != '>') if (s0 != s1) { eb_setmark(w->eb, SelBeg, -1u); /* clear selection */ eb_setmark(w->eb, SelEnd, -1u); } eb_commit(w->eb); - if (r->ob) - ev_register((Evnt){pin[1], EWrite, runev, r}); - else + if (t->ob) { + t->wfd = pin[1]; + ev_register(t->wfd, EWrite, runev, t); + } else { + t->wfd = -1; close(pin[1]); - ev_register((Evnt){pout[0], ERead, runev, r}); - + } + t->rfd = pout[0]; + ev_register(t->rfd, ERead, runev, t); + t->next = t->eb->tasks; + t->eb->tasks = t; return 0; } @@ -2,3 +2,4 @@ int ex_run(W *, unsigned); int ex_look(W *, Rune *, unsigned); int ex_put(EBuf *, char *); int ex_get(W *, char *); +void ex_cancel(Task *); @@ -43,7 +43,7 @@ resetclicks() clicks = 0; } -static int +static void gev(int fd, int flag, void *unused) { enum { @@ -141,7 +141,6 @@ gev(int fd, int flag, void *unused) curwin->cu = p0; curwin->dirty = 1; } - return 0; } int @@ -151,9 +150,9 @@ main(int ac, char *av[]) g = &gui_x11; guifd = g->init(); - ev_register((Evnt){guifd, ERead, gev, 0}); + ev_register(guifd, ERead, gev, 0); win_init(g); - curwin = win_new(eb_new()); + curwin = win_new(); if (ac > 1) ex_get(curwin, av[1]); gev(0, 0, 0); @@ -9,6 +9,7 @@ #include "edit.h" #include "gui.h" #include "win.h" +#include "exec.h" enum { RingSize = 1 }; /* bigger is (a bit) faster */ @@ -48,7 +49,7 @@ win_init(struct gui *gui) tabw = TabWidth * g->textwidth((Rune[]){' '}, 1); /* initialize the tag */ - tag.win.eb = eb_new(); + tag.win.eb = eb_new(-1); eb_ins_utf8(tag.win.eb, 0, (unsigned char *)TagInit, sizeof TagInit - 1); /* the gui module does not give a way to access the screen @@ -64,12 +65,11 @@ win_init(struct gui *gui) * windows), 0 is returned. */ W * -win_new(EBuf *eb) +win_new() { W *w, *w1; int i, x, size; - assert(eb); for (w1=wins;; w1++) { if (w1 - wins >= MaxWins) return 0; @@ -87,7 +87,7 @@ win_new(EBuf *eb) x = w->rectx + w->rectw + g->border; i++; } - w1->eb = eb; + w1->eb = eb_new(-1); move(w1, x, 0, size, fheight); screen[i] = w1; screen[i+1] = 0; @@ -115,6 +115,8 @@ win_kill(W *w) rx = w1->rectx; } move(w1, rx, 0, w->rectw+g->border+w1->rectw, fheight); + while (w->eb->tasks) + ex_cancel(w->eb->tasks); eb_kill(w->eb); memset(w, 0, sizeof(W)); w->eb = 0; @@ -625,8 +627,11 @@ int main() eb = eb_new(); gui_x11.init(); win_init(&gui_x11); - for (int i = 0; i < N; i++) - ws[i] = win_new(eb); + for (int i = 0; i < N; i++) { + ws[i] = win_new(); + eb_kill(ws[i]->eb); + ws[i]->eb = eb; + } w = ws[0]; for (int i=0; i<5; i++) @@ -32,7 +32,7 @@ struct w { enum CursorLoc { CTop, CMid, CBot }; void win_init(struct gui *g); -W *win_new(EBuf *eb); +W *win_new(void); W *win_kill(W *); unsigned win_at(W *w, int x, int y); W *win_which(int x, int y); |
