summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Carbonneaux2014-09-19 14:56:24 -0400
committerQuentin Carbonneaux2014-09-19 15:07:49 -0400
commit371dd1e4f7cf8adb72cf5bbde40b89b0724740c7 (patch)
treee22d1f416dcbbc11607ba54372ea8088413bbb98
parent3aa9b35a7fff2975558204b45ca3184c55ecff51 (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.c39
-rw-r--r--edit.h14
-rw-r--r--evnt.c48
-rw-r--r--evnt.h12
-rw-r--r--exec.c172
-rw-r--r--exec.h1
-rw-r--r--main.c7
-rw-r--r--win.c17
-rw-r--r--win.h2
9 files changed, 158 insertions, 154 deletions
diff --git a/edit.c b/edit.c
index 470f3b6..3e49bfd 100644
--- a/edit.c
+++ b/edit.c
@@ -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
diff --git a/edit.h b/edit.h
index 70f984b..0983020 100644
--- a/edit.h
+++ b/edit.h
@@ -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);
diff --git a/evnt.c b/evnt.c
index a823e66..b8dd4e3 100644
--- a/evnt.c
+++ b/evnt.c
@@ -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)
{
diff --git a/evnt.h b/evnt.h
index b89c974..3ebcad2 100644
--- a/evnt.h
+++ b/evnt.h
@@ -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);
diff --git a/exec.c b/exec.c
index 8a6202c..53a2623 100644
--- a/exec.c
+++ b/exec.c
@@ -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;
}
diff --git a/exec.h b/exec.h
index 3991875..1da01a3 100644
--- a/exec.h
+++ b/exec.h
@@ -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 *);
diff --git a/main.c b/main.c
index 805e3b3..dd2cb3d 100644
--- a/main.c
+++ b/main.c
@@ -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);
diff --git a/win.c b/win.c
index 614be10..94edc56 100644
--- a/win.c
+++ b/win.c
@@ -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++)
diff --git a/win.h b/win.h
index 00fd94b..98835f9 100644
--- a/win.h
+++ b/win.h
@@ -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);