about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog4
-rw-r--r--Src/builtin.c1
-rw-r--r--Src/exec.c4
-rw-r--r--Src/init.c2
-rw-r--r--Src/parse.c59
-rw-r--r--Src/text.c5
-rw-r--r--Src/zsh.h18
7 files changed, 73 insertions, 20 deletions
diff --git a/ChangeLog b/ChangeLog
index b9272bbbc..2b27065bb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2002-06-07  Peter Stephenson  <pws@csr.com>
 
+	* 17299: Src/builtin.c, Src/exec.c, Src/init.c, Src/parse.c,
+	Src/text.c, Src/zsh.h: Instead of freeing Eprogs in one go
+	at the top level, use reference counts.
+
 	* 17301 (doc slightly tweaked): Src/Zle/zle_thingy.c,
 	Doc/Zsh/zle.yo: make `zle -I' only invalidate the display on the
 	first call to prevent spurious extra command lines appearing.
diff --git a/Src/builtin.c b/Src/builtin.c
index 90681fc3a..60971b9e1 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -2322,6 +2322,7 @@ mkautofn(Shfunc shf)
     p->strs = NULL;
     p->shf = shf;
     p->npats = 0;
+    p->nref = 1; /* allocated from permanent storage */
     p->pats = (Patprog *) p->prog;
     p->flags = EF_REAL;
     p->dump = NULL;
diff --git a/Src/exec.c b/Src/exec.c
index 4888c9fb8..04a141679 100644
--- a/Src/exec.c
+++ b/Src/exec.c
@@ -725,8 +725,11 @@ execode(Eprog p, int dont_change_job, int exiting)
     s.prog = p;
     s.pc = p->prog;
     s.strs = p->strs;
+    useeprog(p);		/* Mark as in use */
 
     execlist(&s, dont_change_job, exiting);
+
+    freeeprog(p);		/* Free if now unused */
 }
 
 /* Execute a simplified command. This is used to execute things that
@@ -3134,6 +3137,7 @@ execfuncdef(Estate state, int do_exec)
     while ((s = (char *) ugetnode(names))) {
 	prog = (Eprog) zalloc(sizeof(*prog));
 	prog->npats = npats;
+	prog->nref = 1; /* allocated from permanent storage */
 	prog->len = len;
 	if (state->prog->dump) {
 	    prog->flags = EF_MAP;
diff --git a/Src/init.c b/Src/init.c
index e4f77e4b3..ac0885f10 100644
--- a/Src/init.c
+++ b/Src/init.c
@@ -163,8 +163,6 @@ loop(int toplevel, int justonce)
 	    if (stopmsg)	/* unset 'you have stopped jobs' flag */
 		stopmsg--;
 	    execode(prog, 0, 0);
-	    if (toplevel)
-		freeeprogs();
 	    tok = toksav;
 	    if (toplevel)
 		noexitct = 0;
diff --git a/Src/parse.c b/Src/parse.c
index e0561605b..e44129608 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -396,6 +396,7 @@ bld_eprog(void)
 		(ecused * sizeof(wordcode)) +
 		ecsoffs);
     ret->npats = ecnpats;
+    ret->nref = -1;		/* Eprog is on the heap */
     ret->pats = (Patprog *) zhalloc(ret->len);
     ret->prog = (Wordcode) (ret->pats + ecnpats);
     ret->strs = (char *) (ret->prog + ecused);
@@ -2085,6 +2086,12 @@ dupeprog(Eprog p, int heap)
     r->dump = NULL;
     r->len = p->len;
     r->npats = p->npats;
+    /*
+     * If Eprog is on the heap, reference count is not valid.
+     * Otherwise, initialise reference count to 1 so that a freeeprog()
+     * will delete it if it is not in use.
+     */
+    r->nref = heap ? -1 : 1;
     pp = r->pats = (heap ? (Patprog *) hcalloc(r->len) :
 		    (Patprog *) zcalloc(r->len));
     r->prog = (Wordcode) (r->pats + r->npats);
@@ -2098,33 +2105,49 @@ dupeprog(Eprog p, int heap)
     return r;
 }
 
-static LinkList eprog_free;
+
+/*
+ * Pair of functions to mark an Eprog as in use, and to delete it
+ * when it is no longer in use, by means of the reference count in
+ * then nref element.
+ *
+ * If nref is negative, the Eprog is on the heap and is never freed.
+ */
+
+/* Increase the reference count of an Eprog so it won't be deleted. */
 
 /**/
 mod_export void
-freeeprog(Eprog p)
+useeprog(Eprog p)
 {
-    if (p && p != &dummy_eprog)
-	zaddlinknode(eprog_free, p);
+    if (p && p != &dummy_eprog && p->nref >= 0)
+	p->nref++;
 }
 
+/* Free an Eprog if we have finished with it */
+
 /**/
-void
-freeeprogs(void)
+mod_export void
+freeeprog(Eprog p)
 {
-    Eprog p;
     int i;
     Patprog *pp;
 
-    while ((p = (Eprog) getlinknode(eprog_free))) {
-	for (i = p->npats, pp = p->pats; i--; pp++)
-	    freepatprog(*pp);
-	if (p->dump) {
-	    decrdumpcount(p->dump);
-	    zfree(p->pats, p->npats * sizeof(Patprog));
-	} else
-	    zfree(p->pats, p->len);
-	zfree(p, sizeof(*p));
+    if (p && p != &dummy_eprog) {
+	/* paranoia */
+	DPUTS(p->nref > 0 && (p->flags & EF_HEAP), "Heap EPROG has nref > 0");
+	DPUTS(p->nref < 0 && !(p->flags & EF_HEAP), "Real EPROG has nref < 0");
+	DPUTS(p->nref < -1 || p->nref > 256, "Uninitialised EPROG nref");
+	if (p->nref > 0 && !--p->nref) {
+	    for (i = p->npats, pp = p->pats; i--; pp++)
+		freepatprog(*pp);
+	    if (p->dump) {
+		decrdumpcount(p->dump);
+		zfree(p->pats, p->npats * sizeof(Patprog));
+	    } else
+		zfree(p->pats, p->len);
+	    zfree(p, sizeof(*p));
+	}
     }
 }
 
@@ -2268,8 +2291,6 @@ init_eprog(void)
     dummy_eprog.len = sizeof(wordcode);
     dummy_eprog.prog = &dummy_eprog_code;
     dummy_eprog.strs = NULL;
-
-    eprog_free = znewlinklist();
 }
 
 /* Code for function dump files.
@@ -3057,6 +3078,7 @@ check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
 	    prog->flags = EF_MAP;
 	    prog->len = h->len;
 	    prog->npats = np = h->npats;
+	    prog->nref = 1;	/* allocated from permanent storage */
 	    prog->pats = pp = (Patprog *) zalloc(np * sizeof(Patprog));
 	    prog->prog = f->map + h->start;
 	    prog->strs = ((char *) prog->prog) + h->strs;
@@ -3108,6 +3130,7 @@ check_dump_file(char *file, struct stat *sbuf, char *name, int *ksh)
 	    prog->flags = EF_REAL;
 	    prog->len = h->len + po;
 	    prog->npats = np = h->npats;
+	    prog->nref = 1; /* allocated from permanent storage */
 	    prog->pats = pp = (Patprog *) d;
 	    prog->prog = (Wordcode) (((char *) d) + po);
 	    prog->strs = ((char *) prog->prog) + h->strs;
diff --git a/Src/text.c b/Src/text.c
index 44527666d..6cb734af2 100644
--- a/Src/text.c
+++ b/Src/text.c
@@ -117,6 +117,8 @@ getpermtext(Eprog prog, Wordcode c)
     if (!c)
 	c = prog->prog;
 
+    useeprog(prog);		/* mark as used */
+
     s.prog = prog;
     s.pc = c;
     s.strs = prog->strs;
@@ -130,6 +132,7 @@ getpermtext(Eprog prog, Wordcode c)
     if (prog->len)
 	gettext2(&s);
     *tptr = '\0';
+    freeeprog(prog);		/* mark as unused */
     untokenize(tbuf);
     return tbuf;
 }
@@ -147,6 +150,7 @@ getjobtext(Eprog prog, Wordcode c)
     if (!c)
 	c = prog->prog;
 
+    useeprog(prog);		/* mark as used */
     s.prog = prog;
     s.pc = c;
     s.strs = prog->strs;
@@ -159,6 +163,7 @@ getjobtext(Eprog prog, Wordcode c)
     tjob = 1;
     gettext2(&s);
     *tptr = '\0';
+    freeeprog(prog);		/* mark as unused */
     untokenize(jbuf);
     return jbuf;
 }
diff --git a/Src/zsh.h b/Src/zsh.h
index 28a3c20e9..15b4c404a 100644
--- a/Src/zsh.h
+++ b/Src/zsh.h
@@ -497,10 +497,28 @@ struct funcdump {
     char *filename;
 };
 
+/*
+ * A note on the use of reference counts in Eprogs.
+ *
+ * When an Eprog is created, nref is set to -1 if the Eprog is on the
+ * heap; then no attempt is ever made to free it.  (This information is
+ * already present in EF_HEAP; we use the redundancy for debugging
+ * checks.)
+ *
+ * Otherwise, nref is initialised to 1.  Calling freeprog() decrements
+ * nref and frees the Eprog if the count is now zero.  When the Eprog
+ * is in use, we call useeprog() at the start and freeprog() at the
+ * end to increment and decrement the reference counts.  If an attempt
+ * is made to free the Eprog from within, this will then take place
+ * when execution is finished, typically in the call to freeeprog()
+ * in execode().  If the Eprog was on the heap, neither useeprog()
+ * nor freeeprog() has any effect.
+ */
 struct eprog {
     int flags;			/* EF_* below */
     int len;			/* total block length */
     int npats;			/* Patprog cache size */
+    int nref;			/* number of references: delete when zero */
     Patprog *pats;		/* the memory block, the patterns */
     Wordcode prog;		/* memory block ctd, the code */
     char *strs;			/* memory block ctd, the strings */