2000-07-02
2000-07-02 02:24:06 by Martin Stjernholm <mast@lysator.liu.se>
-
af72c5c45274761a03e3584158b0a44532ed47a7
(203 lines)
(+107/-96)
[
Show
| Annotate
]
Branch: 7.9
Fixed bugs when gc'ing strong links, among other things.
Rev: src/gc.c:1.98
Rev: src/gc.h:1.51
29:
#include "block_alloc.h"
- RCSID("$Id: gc.c,v 1.97 2000/06/16 23:43:49 hubbe Exp $");
+ RCSID("$Id: gc.c,v 1.98 2000/07/02 02:24:06 mast Exp $");
/* Run garbage collect approximately every time
* 20 percent of all arrays, objects and programs is
1045:
static int gc_cycle_indent = 0;
#define CYCLE_DEBUG_MSG(M, TXT) do { \
fprintf(stderr, "%*s%-33s %p [%p] ", gc_cycle_indent, "", \
- (TXT), (M)->data, gc_rec_last->data); \
+ (TXT), (M) ? (M)->data : 0, gc_rec_last->data); \
describe_marker(M); \
} while (0)
#else
#define CYCLE_DEBUG_MSG(M, TXT) do {} while (0)
#endif
- static void break_cycle (struct marker *beg, struct marker *pos)
+ static struct marker *break_cycle (struct marker *beg, struct marker *pos)
{
/* There's a cycle from beg to gc_rec_last which should be broken at
* pos. Do it by switching places of the markers before and after pos. */
1063:
#endif
if (beg->cycle) {
- #ifdef PIKE_DEBUG
- if (pos->cycle == beg->cycle || gc_rec_last->cycle == beg->cycle)
- gc_fatal(pos->data, 0, "Same cycle on both sides of broken link.\n");
- #endif
- for (p = &rec_list; p->link->cycle != beg->cycle; p = p->link) {}
- beg = p->link;
+ for (q = &rec_list; q->link->cycle != beg->cycle; q = q->link) {}
+ beg = q->link;
+ if (beg->cycle == pos->cycle) {
+ /* Breaking something previously marked as a cycle. Clear it
+ * since we're no longer sure it's an ambigious cycle. */
+ unsigned cycle = beg->cycle;
+ for (p = beg; p && p->cycle == cycle; p = p->link)
+ p->cycle = 0;
}
-
+ }
else
- for (p = &rec_list; p->link != beg; p = p->link) {}
+ for (q = &rec_list; q->link != beg; q = q->link) {}
- CYCLE_DEBUG_MSG(beg, "break_cycle, break from");
- CYCLE_DEBUG_MSG(pos, "break_cycle, break at");
+ q->link = pos;
- p->link = pos;
+ CYCLE_DEBUG_MSG(beg, "> break_cycle, begin at");
for (p = beg; p->link != pos; p = p->link) {}
for (q = pos;; q = q->link) {
q->flags |= GC_MOVED_BACK|GC_DONT_POP;
- CYCLE_DEBUG_MSG(q, "break_cycle, mark for don't pop");
+ CYCLE_DEBUG_MSG(q, "> break_cycle, mark for don't pop");
if (q == gc_rec_last) break;
}
1091:
q->link = beg;
p->link = m;
}
+
+ if (beg->flags & GC_WEAK_REF) {
+ beg->flags &= ~GC_WEAK_REF;
+ pos->flags |= GC_WEAK_REF;
+ CYCLE_DEBUG_MSG(pos, "> break_cycle, moved weak flag");
}
-
+ return beg;
+ }
+
int gc_cycle_push(void *x, struct marker *m, int weak)
{
#ifdef PIKE_DEBUG
1156: Inside #if defined(GC_CYCLE_DEBUG)
gc_rec_last->flags &= ~GC_LIVE_RECURSE;
#ifdef GC_CYCLE_DEBUG
gc_cycle_indent -= 2;
- CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_push, unwinding live");
+ CYCLE_DEBUG_MSG(gc_rec_last, "> gc_cycle_push, unwinding live");
#endif
gc_rec_last = (struct marker *)
dequeue_lifo(&gc_mark_queue, (queue_call) gc_set_rec_last);
1176:
}
#ifdef PIKE_DEBUG
- if (weak < 0 && gc_rec_last->flags & GC_FOLLOWED_NONSTRONG)
- gc_fatal(x, 0, "Followed strong link too late.\n");
- if (weak >= 0) gc_rec_last->flags |= GC_FOLLOWED_NONSTRONG;
+ if (weak >= 0 && gc_rec_last->flags & GC_FOLLOWED_STRONG)
+ gc_fatal(x, 0, "Followed strong link too early.\n");
+ if (weak < 0) gc_rec_last->flags |= GC_FOLLOWED_STRONG;
#endif
if (m->flags & GC_IN_REC_LIST) { /* A cyclic reference is found. */
1191:
struct marker *p, *weak_ref = 0, *nonstrong_ref = 0;
if (!weak) {
struct marker *q;
+ CYCLE_DEBUG_MSG(m, "gc_cycle_push, search normal");
for (q = m, p = m->link; p; q = p, p = p->link) {
if (p->flags & (GC_WEAK_REF|GC_STRONG_REF)) {
if (p->flags & GC_WEAK_REF) weak_ref = p;
1201:
}
else if (weak < 0) {
+ CYCLE_DEBUG_MSG(m, "gc_cycle_push, search strong");
for (p = m->link; p; p = p->link) {
if (p->flags & GC_WEAK_REF) weak_ref = p;
if (!(p->flags & GC_STRONG_REF)) nonstrong_ref = p;
if (p == gc_rec_last) break;
}
#ifdef PIKE_DEBUG
- if (!nonstrong_ref)
+ if (p == gc_rec_last && !nonstrong_ref)
gc_fatal(x, 0, "Only strong links in cycle.\n");
#endif
}
else {
struct marker *q;
-
+ CYCLE_DEBUG_MSG(m, "gc_cycle_push, search weak");
for (q = m, p = m->link; p; q = p, p = p->link) {
if (!(p->flags & GC_WEAK_REF) && !nonstrong_ref) nonstrong_ref = q;
if (p == gc_rec_last) break;
1226:
* or more weak links in the cycle. Let's break it at the
* last one (to ensure that a sequence of several weak links
* are broken at the last one). */
- CYCLE_DEBUG_MSG(m, "gc_cycle_push, weak break");
+ CYCLE_DEBUG_MSG(weak_ref, "gc_cycle_push, weak break");
break_cycle (m, weak_ref);
}
else if (weak < 0) {
/* The backward link is strong. Must break the cycle at the
* last nonstrong link. */
- CYCLE_DEBUG_MSG(m, "gc_cycle_push, nonstrong break");
+ if (m->flags & GC_STRONG_REF)
+ nonstrong_ref->flags =
+ (nonstrong_ref->flags & ~GC_WEAK_REF) | GC_STRONG_REF;
+ else
+ m->flags = (m->flags & ~GC_WEAK_REF) | GC_STRONG_REF;
+ CYCLE_DEBUG_MSG(nonstrong_ref, "gc_cycle_push, nonstrong break");
break_cycle (m, nonstrong_ref);
- if (m->flags & GC_STRONG_REF) nonstrong_ref->flags |= GC_STRONG_REF;
+
}
else if (nonstrong_ref) {
1243:
* cycle with a nonweak link in it. Break before the first
* link that's stronger than the others. */
if (nonstrong_ref != m) {
- CYCLE_DEBUG_MSG(m, "gc_cycle_push, weaker break");
+ CYCLE_DEBUG_MSG(nonstrong_ref, "gc_cycle_push, weaker break");
break_cycle (m, nonstrong_ref);
}
}
- else {
- /* A normal or weak cycle which will be destructed in
- * arbitrary order. */
+ else if (!weak) {
+ /* A normal cycle which will be destructed in arbitrary
+ * order. For reasons having to do with strong links we
+ * can't mark weak cycles this way. */
unsigned cycle = m->cycle ? m->cycle : ++last_cycle;
if (cycle == gc_rec_last->cycle)
CYCLE_DEBUG_MSG(m, "gc_cycle_push, old cycle");
else {
unsigned replace_cycle = gc_rec_last->cycle;
CYCLE_DEBUG_MSG(m, "gc_cycle_push, cycle");
- for (p = m; p != gc_rec_last; p = p->link) {
+ for (p = m;; p = p->link) {
p->cycle = cycle;
- CYCLE_DEBUG_MSG(p, "gc_cycle_push, mark cycle");
+ CYCLE_DEBUG_MSG(p, "> gc_cycle_push, mark cycle");
+ if (p == gc_rec_last) break;
}
- if (replace_cycle != cycle)
- for (; p && p->cycle == replace_cycle; p = p->link) {
+ if (replace_cycle && replace_cycle != cycle)
+ for (p = p->link; p && p->cycle == replace_cycle; p = p->link) {
p->cycle = cycle;
- CYCLE_DEBUG_MSG(p, "gc_cycle_push, re-mark cycle");
+ CYCLE_DEBUG_MSG(p, "> gc_cycle_push, re-mark cycle");
}}}} /* Mmm.. lisp ;) */
- else /* A forward reference. */
- if (m->flags & GC_ON_STACK) {
- /* It's a reference to a marker that has been swapped
- * further down the list by break_cycle(). In that case we
- * must mark gc_rec_last to stay on the list. */
- CYCLE_DEBUG_MSG(m, "gc_cycle_push, mark for don't pop");
+ else { /* A forward reference. */
+ CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref");
+ if (m->flags & GC_ON_STACK &&
+ !(gc_rec_last->cycle && gc_rec_last->cycle == m->cycle)) {
+ /* Since it's not part of the same cycle it's a reference to
+ * a marker that has been swapped further down the list by
+ * break_cycle(). In that case we must mark gc_rec_last to
+ * stay on the list. */
gc_rec_last->flags |= GC_DONT_POP;
-
+ CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_push, mark for don't pop");
}
- else
- CYCLE_DEBUG_MSG(m, "gc_cycle_push, forward ref");
+
}
}
-
+ }
else
if (!(m->flags & GC_CYCLE_CHECKED)) {
1297:
#endif
p = gc_rec_last;
- if (gc_rec_last->cycle)
- for (; p->link && p->link->cycle == gc_rec_last->cycle; p = p->link) {}
+ if (p->cycle)
+ for (; p->link && p->link->cycle == p->cycle; p = p->link) {}
m->link = p->link;
p->link = m;
m->flags |= GC_CYCLE_CHECKED|GC_ON_STACK|GC_IN_REC_LIST;
1364:
void gc_cycle_pop(void *a)
{
- struct marker *m = find_marker(a), *p, *q;
+ struct marker *m = find_marker(a), *p, *q, *base;
#ifdef PIKE_DEBUG
if (Pike_in_gc != GC_PASS_CYCLE)
1393: Inside #if defined(PIKE_DEBUG)
if (m->flags & GC_GOT_DEAD_REF)
gc_fatal(a, 0, "Didn't expect a dead extra ref.\n");
#endif
+ m->flags &= ~GC_ON_STACK;
if (m->flags & GC_DONT_POP) {
#ifdef PIKE_DEBUG
if (!m->link)
- gc_fatal(a, 0, "Found GC_DONT_POP marker on top of stack.\n");
+ gc_fatal(a, 0, "Found GC_DONT_POP marker on top of list.\n");
#endif
- CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep on stack");
+ CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep in list");
if (!(m->flags & GC_MOVED_BACK)) {
gc_rec_last->flags |= GC_DONT_POP;
CYCLE_DEBUG_MSG(gc_rec_last, "gc_cycle_pop, propagate don't pop");
1407:
return;
}
- q = gc_rec_last;
+ for (base = gc_rec_last, p = base->link;; base = p, p = p->link) {
+ if (base == m) {
+ #ifdef GC_CYCLE_DEBUG
+ CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep cycle");
+ #endif
+ return;
+ }
+ if (!(p->cycle && p->cycle == base->cycle))
+ break;
+ }
+
+ q = base;
while (1) {
p = q->link;
#ifdef PIKE_DEBUG
- if (p->flags & GC_LIVE_RECURSE)
- gc_fatal(p->data, 0, "Marker still got GC_LIVE_RECURSE at pop.\n");
+ if (p->flags & (GC_ON_STACK|GC_LIVE_RECURSE))
+ gc_fatal(p->data, 0, "Marker still got an on-stack flag at pop.\n");
#endif
if (!p->cycle && !(p->flags & GC_LIVE_OBJ)) {
ADD_REF_IF_DEAD(p);
- p->flags &= ~(GC_ON_STACK|GC_IN_REC_LIST|GC_DONT_POP|
- GC_WEAK_REF|GC_STRONG_REF);
+ p->flags &= ~(GC_IN_REC_LIST|GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF);
q->link = p->link;
DO_IF_DEBUG(p->link = (struct marker *) -1);
CYCLE_DEBUG_MSG(p, "gc_cycle_pop, pop off");
}
-
+ else q = p;
- else {
- p->flags &= ~(GC_ON_STACK|GC_DONT_POP|
- GC_WEAK_REF|GC_STRONG_REF);
- q = p;
- }
+
if (p == m) break;
}
- if (gc_rec_last != q) {
- if (!q->cycle || gc_rec_last->cycle != q->cycle) {
- /* If the thing(s) are part of a cycle that we aren't leaving,
- * we let them stay on the list so the whole cycle is popped at
- * once. Otherwise it's time to move live objects to the kill
- * list. */
- struct marker *base;
- #ifdef PIKE_DEBUG
- int cycle = q->cycle;
- #endif
-
- base = gc_rec_last;
- if (gc_rec_last->cycle)
- for (; base->link->cycle == gc_rec_last->cycle; base = base->link) {}
-
+ if (base != q) {
q = base;
while ((p = q->link)) {
#ifdef PIKE_DEBUG
- if (p->cycle && cycle && p->cycle != cycle)
- gc_fatal(p->data, 0, "Popping more than one cycle from rec_list.\n");
+
if (!(p->flags & GC_IN_REC_LIST))
gc_fatal(p->data, 0, "Marker being cycle popped doesn't have GC_IN_REC_LIST.\n");
if (p->flags & GC_GOT_DEAD_REF)
1459:
#endif
p->flags &= ~GC_IN_REC_LIST;
+ p->flags &= ~(GC_DONT_POP|GC_WEAK_REF|GC_STRONG_REF);
if (p->flags & GC_LIVE_OBJ) {
/* This extra ref is taken away in the kill pass. */
gc_add_extra_ref(p->data);
1477:
kill_list = base->link;
base->link = p;
}
- else
- CYCLE_DEBUG_MSG(m, "gc_cycle_pop, keep in cycle");
+
}
- }
+
void gc_set_rec_last(struct marker *m)
{