summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQuentin Carbonneaux2014-09-04 15:10:11 -0400
committerQuentin Carbonneaux2014-09-04 15:10:11 -0400
commit2ad2f1a599c16f8015f4af8a29f75c68c24c5574 (patch)
treeb6f0e9e34ff5508cb27eddc5ea33f0da2e9b9b48
parent9497069d9c767965118d1bb9bd6c5b3dc3f8ee38 (diff)
add proper buffer deletion
Because some background processes might run concurrently with the execution of the Del command I added a refcount to edit buffers. Details about the refcount: + When this count c is >=0 it means that the buffer is alive and has c+1 concurrent users (+1 for the window displaying it). + If c<0, the buffer has -c users but is a "zombie". Its parent window was deleted. So the buffer is still in memory but cannot be used, users must drop their pointer after having incremented c. If c reaches 0, the buffer must be freed.
-rw-r--r--edit.c28
-rw-r--r--edit.h4
-rw-r--r--exec.c31
-rw-r--r--win.c7
-rw-r--r--win.h2
5 files changed, 52 insertions, 20 deletions
diff --git a/edit.c b/edit.c
index 6a1e13b..470f3b6 100644
--- a/edit.c
+++ b/edit.c
@@ -190,9 +190,37 @@ eb_new()
eb->redo = log_new();
eb->ml = 0;
eb->path = 0;
+ eb->refs = 0;
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;
+ buf_clr(&eb->b);
+ log_clr(eb->undo);
+ log_clr(eb->redo);
+ while ((m=eb->ml)) {
+ eb->ml = m->next;
+ free(m);
+ }
+ free(eb->b.p);
+ 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.
diff --git a/edit.h b/edit.h
index 8325ee4..70f984b 100644
--- a/edit.h
+++ b/edit.h
@@ -24,12 +24,14 @@ struct ebuf {
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 */
};
EBuf *eb_new(void);
-void eb_del(EBuf *, unsigned, unsigned);
+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);
int eb_ins_utf8(EBuf *, unsigned, unsigned char *, int);
void eb_commit(EBuf *);
diff --git a/exec.c b/exec.c
index 70f2d0e..e4d6ad8 100644
--- a/exec.c
+++ b/exec.c
@@ -335,7 +335,7 @@ new(W *w, EBuf *eb, unsigned p0)
static int
del(W *w, EBuf *eb, unsigned p0)
{
- w = win_delete(w);
+ w = win_kill(w);
if (w)
curwin = w;
else
@@ -363,13 +363,26 @@ runev(int fd, int flag, void *data)
unsigned char buf[2048], *p;
Rune r;
- rn = data; /* XXX rn->eb can be invalid */
+ 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;
+ }
if (flag & ERead) {
assert(rn->eb);
memcpy(buf, rn->in, rn->nin);
n = read(fd, &buf[rn->nin], sizeof buf - rn->nin);
if (n <= 0) {
close(fd);
+ rn->eb->refs--;
rn->eb = 0;
goto Reset;
}
@@ -413,19 +426,6 @@ run(W *w, EBuf *eb, unsigned p0)
int pin[2], pout[2];
Run *r;
- /* ***
- clear (and possibly delete) selection,
- get the "insertion" position and set a mark for it in the
- edit buffer (Acme does not do this, it just stores an offset)
-
- what happens when eb is deleted/changed
- during the command execution?
- + refcount ebs and make eb_free free the
- data and have eb contain simply the refcount
- + when a dummy eb is detected in the callback,
- just abort the IO operation
- *** */
-
eol = buf_eol(&eb->b, p0);
p1 = 1 + skipb(&eb->b, eol-1, -1);
if (p1 == p0)
@@ -487,6 +487,7 @@ run(W *w, EBuf *eb, unsigned p0)
default:
abort();
}
+ r->eb->refs++;
if (ctyp != 0)
if (s0 != s1) {
eb_setmark(w->eb, SelBeg, -1u); /* clear selection */
diff --git a/win.c b/win.c
index 5122864..a704598 100644
--- a/win.c
+++ b/win.c
@@ -94,13 +94,13 @@ win_new(EBuf *eb)
return w1;
}
-/* win_delete - Delete a window created by win_new.
+/* win_kill - Delete a window created by win_new.
* An adjacent window is returned.
*/
W *
-win_delete(W *w)
+win_kill(W *w)
{
- W *w1, *sw;
+ W *w1, **sw;
int rx;
if (!screen[1])
@@ -115,6 +115,7 @@ win_delete(W *w)
rx = w1->rectx;
}
move(w1, rx, 0, w->rectw+g->border+w1->rectw, fheight);
+ eb_kill(w->eb);
memset(w, 0, sizeof(W));
w->eb = 0;
memmove(sw, sw+1, (MaxWins-(sw-screen))*sizeof(W*));
diff --git a/win.h b/win.h
index 111d44b..00fd94b 100644
--- a/win.h
+++ b/win.h
@@ -33,7 +33,7 @@ enum CursorLoc { CTop, CMid, CBot };
void win_init(struct gui *g);
W *win_new(EBuf *eb);
-W *win_delete(W *);
+W *win_kill(W *);
unsigned win_at(W *w, int x, int y);
W *win_which(int x, int y);
void win_move(W *, int x, int y);