From 0e08b8c2e485c355acc4818c1b04a81bc7e150be Mon Sep 17 00:00:00 2001 From: Peter Stephenson Date: Fri, 7 Jun 2002 14:44:21 +0000 Subject: 17299: Use reference counts in Eprogs so as to be able to free them as soon as finished with. --- ChangeLog | 4 ++++ Src/builtin.c | 1 + Src/exec.c | 4 ++++ Src/init.c | 2 -- Src/parse.c | 59 +++++++++++++++++++++++++++++++++++++++++------------------ Src/text.c | 5 +++++ Src/zsh.h | 18 ++++++++++++++++++ 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 + * 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 */ -- cgit 1.4.1