* gc-mark.c (scm_mark_all): Do not rely on hooks to run the weak
hashtable and guardian machinery but call the relevant functions directly. * guardians.h, guardians.c, deprecated.h, deprecated.c (scm_destroy_guardian_x, scm_guardian_greedy_p, scm_guardian_destroyed_p, scm_guard, scm_get_one_zombie): Deprecated and moved into deprecated.[ch]. * guardians.h, guardians.c: Mostly rewritten. (scm_i_init_guardians_for_gc, scm_i_identify_inaccessible_guardeds, scm_i_mark_inaccessible_guardeds): New. * weaks.h, weaks.c (SCM_I_WVECT_TYPE, SCM_I_SET_WVECT_TYPE): New. (SCM_I_WVECT_N_ITEMS, SCM_I_SET_WVECT_N_ITEMS): New. (SCM_WVECTF_NOSCAN, SCM_WVECT_NOSCAN_P): Removed. (scm_weaks_prehistory): Removed. (scm_i_init_weak_vectors_for_gc, scm_i_mark_weak_vector, scm_i_mark_weak_vectors_non_weaks, scm_i_remove_weaks_from_weak_vectors, scm_i_remove_weaks): New. (scm_weak_vector_gc_init, scm_mark_weak_vector_spines, scm_scan_weak_vectors): Removed. * hashtab.h (scm_i_scan_weak_hashtables): New. * hashtab.c (make_hash_table, scm_i_rehash): Do not use SCM_WVECTF_NOSCAN. (hashtable_print): Use SCM_HASHTABLE_N_ITEMS instead of t->n_items. (scan_weak_hashtables, scm_i_scan_weak_hashtables): Renamed former to latter. Do not scan the alists themselves, this is done by the weak vector code now. Just update the element count. * vectors.h (SCM_I_WVECT_TYPE, SCM_I_WVECT_EXTRA): Renamed former to latter. The type is now only part of the cell word. (SCM_I_SET_WVECT_TYPE, SCM_I_SET_WVECT_EXTRA): Likewise. * init.c (scm_i_init_guile): Do not call scm_weaks_prehistory.
This commit is contained in:
parent
5070fd11b2
commit
06c1d90009
11 changed files with 519 additions and 703 deletions
|
|
@ -1439,6 +1439,59 @@ scm_i_defer_ints_etc ()
|
||||||
"Use a mutex instead if appropriate.");
|
"Use a mutex instead if appropriate.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_guard (SCM guardian, SCM obj, int throw_p)
|
||||||
|
{
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("scm_guard is deprecated. Use scm_call_1 instead.");
|
||||||
|
|
||||||
|
return scm_call_1 (guardian, obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM
|
||||||
|
scm_get_one_zombie (SCM guardian)
|
||||||
|
{
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("scm_guard is deprecated. Use scm_call_0 instead.");
|
||||||
|
|
||||||
|
return scm_call_0 (guardian);
|
||||||
|
}
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_guardian_destroyed_p, "guardian-destroyed?", 1, 0, 0,
|
||||||
|
(SCM guardian),
|
||||||
|
"Return @code{#t} if @var{guardian} has been destroyed, otherwise @code{#f}.")
|
||||||
|
#define FUNC_NAME s_scm_guardian_destroyed_p
|
||||||
|
{
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("'guardian-destroyed?' is deprecated.");
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_guardian_greedy_p, "guardian-greedy?", 1, 0, 0,
|
||||||
|
(SCM guardian),
|
||||||
|
"Return @code{#t} if @var{guardian} is a greedy guardian, otherwise @code{#f}.")
|
||||||
|
#define FUNC_NAME s_scm_guardian_greedy_p
|
||||||
|
{
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("'guardian-greedy?' is deprecated.");
|
||||||
|
return SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_destroy_guardian_x, "destroy-guardian!", 1, 0, 0,
|
||||||
|
(SCM guardian),
|
||||||
|
"Destroys @var{guardian}, by making it impossible to put any more\n"
|
||||||
|
"objects in it or get any objects from it. It also unguards any\n"
|
||||||
|
"objects guarded by @var{guardian}.")
|
||||||
|
#define FUNC_NAME s_scm_destroy_guardian_x
|
||||||
|
{
|
||||||
|
scm_c_issue_deprecation_warning
|
||||||
|
("'destroy-guardian!' is deprecated and ineffective.");
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
|
}
|
||||||
|
#undef FUNC_NAME
|
||||||
|
|
||||||
void
|
void
|
||||||
scm_i_init_deprecated ()
|
scm_i_init_deprecated ()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -538,22 +538,25 @@ SCM_API SCM_STACKITEM *scm_i_stack_base (void);
|
||||||
#define SCM_FLUIDP(x) scm_i_fluidp (x)
|
#define SCM_FLUIDP(x) scm_i_fluidp (x)
|
||||||
SCM_API int scm_i_fluidp (SCM x);
|
SCM_API int scm_i_fluidp (SCM x);
|
||||||
|
|
||||||
/* In the old days, SCM_CRITICAL_SECTION_START stopped signal handlers from running,
|
/* In the old days, SCM_CRITICAL_SECTION_START stopped signal handlers
|
||||||
since in those days the handler directly ran scheme code, and that had to
|
from running, since in those days the handler directly ran scheme
|
||||||
be avoided when the heap was not in a consistent state etc. And since
|
code, and that had to be avoided when the heap was not in a
|
||||||
the scheme code could do a stack swapping new continuation etc, signals
|
consistent state etc. And since the scheme code could do a stack
|
||||||
had to be deferred around various C library functions which were not safe
|
swapping new continuation etc, signals had to be deferred around
|
||||||
or not known to be safe to swap away, which was a lot of stuff.
|
various C library functions which were not safe or not known to be
|
||||||
|
safe to swap away, which was a lot of stuff.
|
||||||
|
|
||||||
These days signals are implemented with asyncs and don't directly run
|
These days signals are implemented with asyncs and don't directly
|
||||||
scheme code in the handler, but hold it until an SCM_TICK etc where it
|
run scheme code in the handler, but hold it until an SCM_TICK etc
|
||||||
will be safe. This means interrupt protection is not needed and
|
where it will be safe. This means interrupt protection is not
|
||||||
SCM_CRITICAL_SECTION_START / SCM_CRITICAL_SECTION_END is something of an anachronism.
|
needed and SCM_CRITICAL_SECTION_START / SCM_CRITICAL_SECTION_END is
|
||||||
|
something of an anachronism.
|
||||||
|
|
||||||
What past SCM_CRITICAL_SECTION_START usage also did though was indicate code that was
|
What past SCM_CRITICAL_SECTION_START usage also did though was
|
||||||
not reentrant, ie. could not be reentered by signal handler code. The
|
indicate code that was not reentrant, ie. could not be reentered by
|
||||||
present definitions are a mutex lock, affording that reentrancy
|
signal handler code. The present definitions are a mutex lock,
|
||||||
protection against the new guile 1.8 free-running posix threads.
|
affording that reentrancy protection against the new guile 1.8
|
||||||
|
free-running posix threads.
|
||||||
|
|
||||||
One big problem with the present defintions though is that code which
|
One big problem with the present defintions though is that code which
|
||||||
throws an error from within a DEFER/ALLOW region will leave the
|
throws an error from within a DEFER/ALLOW region will leave the
|
||||||
|
|
@ -567,6 +570,17 @@ SCM_API void scm_i_defer_ints_etc (void);
|
||||||
#define SCM_REDEFER_INTS scm_i_defer_ints_etc ()
|
#define SCM_REDEFER_INTS scm_i_defer_ints_etc ()
|
||||||
#define SCM_REALLOW_INTS scm_i_defer_ints_etc ()
|
#define SCM_REALLOW_INTS scm_i_defer_ints_etc ()
|
||||||
|
|
||||||
|
/* Deprecated since they are unnecessary and had not been documented.
|
||||||
|
*/
|
||||||
|
SCM_API SCM scm_guard (SCM guardian, SCM obj, int throw_p);
|
||||||
|
SCM_API SCM scm_get_one_zombie (SCM guardian);
|
||||||
|
|
||||||
|
/* Deprecated since guardians no longer have these special features.
|
||||||
|
*/
|
||||||
|
SCM_API SCM scm_destroy_guardian_x (SCM guardian);
|
||||||
|
SCM_API SCM scm_guardian_greedy_p (SCM guardian);
|
||||||
|
SCM_API SCM scm_guardian_destroyed_p (SCM guardian);
|
||||||
|
|
||||||
void scm_i_init_deprecated (void);
|
void scm_i_init_deprecated (void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ extern unsigned long * __libc_ia64_register_backing_store_base;
|
||||||
#include "libguile/validate.h"
|
#include "libguile/validate.h"
|
||||||
#include "libguile/deprecation.h"
|
#include "libguile/deprecation.h"
|
||||||
#include "libguile/gc.h"
|
#include "libguile/gc.h"
|
||||||
|
#include "libguile/guardians.h"
|
||||||
|
|
||||||
#ifdef GUILE_DEBUG_MALLOC
|
#ifdef GUILE_DEBUG_MALLOC
|
||||||
#include "libguile/debug-malloc.h"
|
#include "libguile/debug-malloc.h"
|
||||||
|
|
@ -70,7 +71,9 @@ void
|
||||||
scm_mark_all (void)
|
scm_mark_all (void)
|
||||||
{
|
{
|
||||||
long j;
|
long j;
|
||||||
|
|
||||||
|
scm_i_init_weak_vectors_for_gc ();
|
||||||
|
scm_i_init_guardians_for_gc ();
|
||||||
|
|
||||||
scm_i_clear_mark_space ();
|
scm_i_clear_mark_space ();
|
||||||
|
|
||||||
|
|
@ -95,11 +98,55 @@ scm_mark_all (void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* FIXME: we should have a means to register C functions to be run
|
|
||||||
* in different phases of GC
|
|
||||||
*/
|
|
||||||
scm_mark_subr_table ();
|
scm_mark_subr_table ();
|
||||||
|
|
||||||
|
int loops = 0;
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
loops++;
|
||||||
|
int again;
|
||||||
|
|
||||||
|
/* Mark the non-weak references of weak vectors. For a weak key
|
||||||
|
alist vector, this would mark the values for keys that are
|
||||||
|
marked. We need to do this in a loop until everything
|
||||||
|
settles down since the newly marked values might be keys in
|
||||||
|
other weak key alist vectors, for example.
|
||||||
|
*/
|
||||||
|
again = scm_i_mark_weak_vectors_non_weaks ();
|
||||||
|
if (again)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Now we scan all marked guardians and move all unmarked objects
|
||||||
|
from the accessible to the inaccessible list.
|
||||||
|
*/
|
||||||
|
scm_i_identify_inaccessible_guardeds ();
|
||||||
|
|
||||||
|
/* When we have identified all inaccessible objects, we can mark
|
||||||
|
them.
|
||||||
|
*/
|
||||||
|
again = scm_i_mark_inaccessible_guardeds ();
|
||||||
|
|
||||||
|
/* This marking might have changed the situation for weak vectors
|
||||||
|
and might have turned up new guardians that need to be processed,
|
||||||
|
so we do it all over again.
|
||||||
|
*/
|
||||||
|
if (again)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Nothing new marked in this round, we are done.
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//fprintf (stderr, "%d loops\n", loops);
|
||||||
|
|
||||||
|
/* Remove all unmarked entries from the weak vectors.
|
||||||
|
*/
|
||||||
|
scm_i_remove_weaks_from_weak_vectors ();
|
||||||
|
|
||||||
|
/* Bring hashtables upto date.
|
||||||
|
*/
|
||||||
|
scm_i_scan_weak_hashtables ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* {Mark/Sweep}
|
/* {Mark/Sweep}
|
||||||
|
|
@ -144,6 +191,7 @@ Perhaps this would work better with an explicit markstack?
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void
|
void
|
||||||
scm_gc_mark_dependencies (SCM p)
|
scm_gc_mark_dependencies (SCM p)
|
||||||
#define FUNC_NAME "scm_gc_mark_dependencies"
|
#define FUNC_NAME "scm_gc_mark_dependencies"
|
||||||
|
|
@ -267,62 +315,7 @@ scm_gc_mark_dependencies (SCM p)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case scm_tc7_wvect:
|
case scm_tc7_wvect:
|
||||||
SCM_I_SET_WVECT_GC_CHAIN (ptr, scm_weak_vectors);
|
scm_i_mark_weak_vector (ptr);
|
||||||
scm_weak_vectors = ptr;
|
|
||||||
if (SCM_IS_WHVEC_ANY (ptr))
|
|
||||||
{
|
|
||||||
long x;
|
|
||||||
long len;
|
|
||||||
int weak_keys;
|
|
||||||
int weak_values;
|
|
||||||
|
|
||||||
len = SCM_SIMPLE_VECTOR_LENGTH (ptr);
|
|
||||||
weak_keys = SCM_WVECT_WEAK_KEY_P (ptr);
|
|
||||||
weak_values = SCM_WVECT_WEAK_VALUE_P (ptr);
|
|
||||||
|
|
||||||
for (x = 0; x < len; ++x)
|
|
||||||
{
|
|
||||||
SCM alist;
|
|
||||||
alist = SCM_SIMPLE_VECTOR_REF (ptr, x);
|
|
||||||
|
|
||||||
/* mark everything on the alist except the keys or
|
|
||||||
* values, according to weak_values and weak_keys. */
|
|
||||||
while ( scm_is_pair (alist)
|
|
||||||
&& !SCM_GC_MARK_P (alist)
|
|
||||||
&& scm_is_pair (SCM_CAR (alist)))
|
|
||||||
{
|
|
||||||
SCM kvpair;
|
|
||||||
SCM next_alist;
|
|
||||||
|
|
||||||
kvpair = SCM_CAR (alist);
|
|
||||||
next_alist = SCM_CDR (alist);
|
|
||||||
/*
|
|
||||||
* Do not do this:
|
|
||||||
* SCM_SET_GC_MARK (alist);
|
|
||||||
* SCM_SET_GC_MARK (kvpair);
|
|
||||||
*
|
|
||||||
* It may be that either the key or value is protected by
|
|
||||||
* an escaped reference to part of the spine of this alist.
|
|
||||||
* If we mark the spine here, and only mark one or neither of the
|
|
||||||
* key and value, they may never be properly marked.
|
|
||||||
* This leads to a horrible situation in which an alist containing
|
|
||||||
* freelist cells is exported.
|
|
||||||
*
|
|
||||||
* So only mark the spines of these arrays last of all marking.
|
|
||||||
* If somebody confuses us by constructing a weak vector
|
|
||||||
* with a circular alist then we are hosed, but at least we
|
|
||||||
* won't prematurely drop table entries.
|
|
||||||
*/
|
|
||||||
if (!weak_keys)
|
|
||||||
scm_gc_mark (SCM_CAR (kvpair));
|
|
||||||
if (!weak_values)
|
|
||||||
scm_gc_mark (SCM_CDR (kvpair));
|
|
||||||
alist = next_alist;
|
|
||||||
}
|
|
||||||
if (SCM_NIMP (alist))
|
|
||||||
scm_gc_mark (alist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case scm_tc7_symbol:
|
case scm_tc7_symbol:
|
||||||
|
|
@ -389,8 +382,8 @@ scm_gc_mark_dependencies (SCM p)
|
||||||
were called with.)
|
were called with.)
|
||||||
*/
|
*/
|
||||||
return ;
|
return ;
|
||||||
|
|
||||||
gc_mark_loop:
|
gc_mark_loop:
|
||||||
if (SCM_IMP (ptr))
|
if (SCM_IMP (ptr))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,14 +23,20 @@
|
||||||
* Programming Language Design and Implementation, June 1993
|
* Programming Language Design and Implementation, June 1993
|
||||||
* ftp://ftp.cs.indiana.edu/pub/scheme-repository/doc/pubs/guardians.ps.gz
|
* ftp://ftp.cs.indiana.edu/pub/scheme-repository/doc/pubs/guardians.ps.gz
|
||||||
*
|
*
|
||||||
|
* Original design: Mikael Djurfeldt
|
||||||
|
* Original implementation: Michael Livshin
|
||||||
|
* Hacked on since by: everybody
|
||||||
|
*
|
||||||
* By this point, the semantics are actually quite different from
|
* By this point, the semantics are actually quite different from
|
||||||
* those described in the abovementioned paper. The semantic changes
|
* those described in the abovementioned paper. The semantic changes
|
||||||
* are there to improve safety and intuitiveness. The interface is
|
* are there to improve safety and intuitiveness. The interface is
|
||||||
* still (mostly) the one described by the paper, however.
|
* still (mostly) the one described by the paper, however.
|
||||||
*
|
*
|
||||||
* Original design: Mikael Djurfeldt
|
* Boiled down again: Marius Vollmer
|
||||||
* Original implementation: Michael Livshin
|
*
|
||||||
* Hacked on since by: everybody
|
* Now they should again behave like those described in the paper.
|
||||||
|
* Scheme guardians should be simple and friendly, not like the greedy
|
||||||
|
* monsters we had...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -43,6 +49,8 @@
|
||||||
#include "libguile/root.h"
|
#include "libguile/root.h"
|
||||||
#include "libguile/hashtab.h"
|
#include "libguile/hashtab.h"
|
||||||
#include "libguile/weaks.h"
|
#include "libguile/weaks.h"
|
||||||
|
#include "libguile/deprecation.h"
|
||||||
|
#include "libguile/eval.h"
|
||||||
|
|
||||||
#include "libguile/guardians.h"
|
#include "libguile/guardians.h"
|
||||||
|
|
||||||
|
|
@ -83,71 +91,105 @@ typedef struct t_guardian
|
||||||
t_tconc live;
|
t_tconc live;
|
||||||
t_tconc zombies;
|
t_tconc zombies;
|
||||||
struct t_guardian *next;
|
struct t_guardian *next;
|
||||||
unsigned long flags;
|
|
||||||
} t_guardian;
|
} t_guardian;
|
||||||
|
|
||||||
#define GUARDIAN_P(x) SCM_SMOB_PREDICATE(tc16_guardian, x)
|
#define GUARDIAN_P(x) SCM_SMOB_PREDICATE(tc16_guardian, x)
|
||||||
#define GUARDIAN_DATA(x) ((t_guardian *) SCM_CELL_WORD_1 (x))
|
#define GUARDIAN_DATA(x) ((t_guardian *) SCM_CELL_WORD_1 (x))
|
||||||
|
|
||||||
#define F_GREEDY 1L
|
static t_guardian *guardians;
|
||||||
#define F_LISTED (1L << 1)
|
|
||||||
#define F_DESTROYED (1L << 2)
|
|
||||||
|
|
||||||
#define GREEDY_P(x) (((x)->flags & F_GREEDY) != 0)
|
void
|
||||||
#define SET_GREEDY(x) ((x)->flags |= F_GREEDY)
|
scm_i_init_guardians_for_gc ()
|
||||||
|
|
||||||
#define LISTED_P(x) (((x)->flags & F_LISTED) != 0)
|
|
||||||
#define SET_LISTED(x) ((x)->flags |= F_LISTED)
|
|
||||||
#define CLR_LISTED(x) ((x)->flags &= ~F_LISTED)
|
|
||||||
|
|
||||||
#define DESTROYED_P(x) (((x)->flags & F_DESTROYED) != 0)
|
|
||||||
#define SET_DESTROYED(x) ((x)->flags |= F_DESTROYED)
|
|
||||||
|
|
||||||
/* during the gc mark phase, live guardians are linked into the lists
|
|
||||||
here. */
|
|
||||||
static t_guardian *greedy_guardians = NULL;
|
|
||||||
static t_guardian *sharing_guardians = NULL;
|
|
||||||
|
|
||||||
static SCM greedily_guarded_whash = SCM_EOL;
|
|
||||||
|
|
||||||
/* this is the list of guarded objects that are parts of cycles. we
|
|
||||||
don't know in which order to return them from guardians, so we just
|
|
||||||
unguard them and whine about it in after-gc-hook */
|
|
||||||
static SCM self_centered_zombies = SCM_EOL;
|
|
||||||
|
|
||||||
|
|
||||||
static void
|
|
||||||
add_to_live_list (t_guardian *g)
|
|
||||||
{
|
{
|
||||||
if (LISTED_P (g))
|
guardians = NULL;
|
||||||
return;
|
|
||||||
|
|
||||||
if (GREEDY_P (g))
|
|
||||||
{
|
|
||||||
g->next = greedy_guardians;
|
|
||||||
greedy_guardians = g;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
g->next = sharing_guardians;
|
|
||||||
sharing_guardians = g;
|
|
||||||
}
|
|
||||||
|
|
||||||
SET_LISTED (g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* mark a guardian by adding it to the live guardian list. */
|
/* mark a guardian by adding it to the live guardian list. */
|
||||||
static SCM
|
static SCM
|
||||||
guardian_mark (SCM ptr)
|
guardian_mark (SCM ptr)
|
||||||
{
|
{
|
||||||
add_to_live_list (GUARDIAN_DATA (ptr));
|
t_guardian *g = GUARDIAN_DATA (ptr);
|
||||||
|
g->next = guardians;
|
||||||
|
guardians = g;
|
||||||
|
|
||||||
/* the objects protected by the guardian are not marked here: that
|
|
||||||
would prevent them from ever getting collected. instead marking
|
|
||||||
is done at the end of the mark phase by guardian_zombify. */
|
|
||||||
return SCM_BOOL_F;
|
return SCM_BOOL_F;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Identify inaccessible objects and move them from the live list to
|
||||||
|
the zombie list. An object is inaccessible when it is unmarked at
|
||||||
|
this point. Therefore, the inaccessible objects are not marked yet
|
||||||
|
since that would prevent them from being recognized as
|
||||||
|
inaccessible.
|
||||||
|
|
||||||
|
The pairs that form the life list itself are marked, tho.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
scm_i_identify_inaccessible_guardeds ()
|
||||||
|
{
|
||||||
|
t_guardian *g;
|
||||||
|
|
||||||
|
for (g = guardians; g; g = g->next)
|
||||||
|
{
|
||||||
|
SCM pair, next_pair;
|
||||||
|
SCM *prev_ptr;
|
||||||
|
|
||||||
|
for (pair = g->live.head, prev_ptr = &g->live.head;
|
||||||
|
!scm_is_eq (pair, g->live.tail);
|
||||||
|
pair = next_pair)
|
||||||
|
{
|
||||||
|
SCM obj = SCM_CAR (pair);
|
||||||
|
next_pair = SCM_CDR (pair);
|
||||||
|
if (!SCM_GC_MARK_P (obj))
|
||||||
|
{
|
||||||
|
/* Unmarked, move to 'inaccessible' list.
|
||||||
|
*/
|
||||||
|
*prev_ptr = next_pair;
|
||||||
|
TCONC_IN (g->zombies, obj, pair);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SCM_SET_GC_MARK (pair);
|
||||||
|
prev_ptr = SCM_CDRLOC (pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCM_SET_GC_MARK (pair);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
scm_i_mark_inaccessible_guardeds ()
|
||||||
|
{
|
||||||
|
t_guardian *g;
|
||||||
|
int again = 0;
|
||||||
|
|
||||||
|
/* We never need to see the guardians again that are processed here,
|
||||||
|
so we clear the list. Calling scm_gc_mark below might find new
|
||||||
|
guardians, however (and other things), and we inform the GC about
|
||||||
|
this by returning non-zero. See scm_mark_all in gc-mark.c
|
||||||
|
*/
|
||||||
|
|
||||||
|
g = guardians;
|
||||||
|
guardians = NULL;
|
||||||
|
|
||||||
|
for (; g; g = g->next)
|
||||||
|
{
|
||||||
|
SCM pair;
|
||||||
|
|
||||||
|
for (pair = g->zombies.head;
|
||||||
|
!scm_is_eq (pair, g->zombies.tail);
|
||||||
|
pair = SCM_CDR (pair))
|
||||||
|
{
|
||||||
|
if (!SCM_GC_MARK_P (pair))
|
||||||
|
{
|
||||||
|
scm_gc_mark (SCM_CAR (pair));
|
||||||
|
SCM_SET_GC_MARK (pair);
|
||||||
|
again = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SCM_SET_GC_MARK (pair);
|
||||||
|
}
|
||||||
|
return again;
|
||||||
|
}
|
||||||
|
|
||||||
static size_t
|
static size_t
|
||||||
guardian_free (SCM ptr)
|
guardian_free (SCM ptr)
|
||||||
|
|
@ -156,39 +198,49 @@ guardian_free (SCM ptr)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
guardian_print (SCM guardian, SCM port, scm_print_state *pstate SCM_UNUSED)
|
guardian_print (SCM guardian, SCM port, scm_print_state *pstate SCM_UNUSED)
|
||||||
{
|
{
|
||||||
t_guardian *g = GUARDIAN_DATA (guardian);
|
t_guardian *g = GUARDIAN_DATA (guardian);
|
||||||
|
|
||||||
scm_puts ("#<", port);
|
scm_puts ("#<guardian ", port);
|
||||||
|
|
||||||
if (DESTROYED_P (g))
|
|
||||||
scm_puts ("destroyed ", port);
|
|
||||||
|
|
||||||
if (GREEDY_P (g))
|
|
||||||
scm_puts ("greedy", port);
|
|
||||||
else
|
|
||||||
scm_puts ("sharing", port);
|
|
||||||
|
|
||||||
scm_puts (" guardian 0x", port);
|
|
||||||
scm_uintprint ((scm_t_bits) g, 16, port);
|
scm_uintprint ((scm_t_bits) g, 16, port);
|
||||||
|
|
||||||
if (! DESTROYED_P (g))
|
scm_puts (" (reachable: ", port);
|
||||||
{
|
scm_display (scm_length (SCM_CDR (g->live.head)), port);
|
||||||
scm_puts (" (reachable: ", port);
|
scm_puts (" unreachable: ", port);
|
||||||
scm_display (scm_length (SCM_CDR (g->live.head)), port);
|
scm_display (scm_length (SCM_CDR (g->zombies.head)), port);
|
||||||
scm_puts (" unreachable: ", port);
|
scm_puts (")", port);
|
||||||
scm_display (scm_length (SCM_CDR (g->zombies.head)), port);
|
|
||||||
scm_puts (")", port);
|
|
||||||
}
|
|
||||||
|
|
||||||
scm_puts (">", port);
|
scm_puts (">", port);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scm_i_guard (SCM guardian, SCM obj)
|
||||||
|
{
|
||||||
|
t_guardian *g = GUARDIAN_DATA (guardian);
|
||||||
|
|
||||||
|
if (!SCM_IMP (obj))
|
||||||
|
{
|
||||||
|
SCM z;
|
||||||
|
z = scm_cons (SCM_BOOL_F, SCM_BOOL_F);
|
||||||
|
TCONC_IN (g->live, obj, z);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SCM
|
||||||
|
scm_i_get_one_zombie (SCM guardian)
|
||||||
|
{
|
||||||
|
t_guardian *g = GUARDIAN_DATA (guardian);
|
||||||
|
SCM res = SCM_BOOL_F;
|
||||||
|
|
||||||
|
if (!TCONC_EMPTYP (g->zombies))
|
||||||
|
TCONC_OUT (g->zombies, res);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/* This is the Scheme entry point for each guardian: If OBJ is an
|
/* This is the Scheme entry point for each guardian: If OBJ is an
|
||||||
* object, it's added to the guardian's live list. If OBJ is unbound,
|
* object, it's added to the guardian's live list. If OBJ is unbound,
|
||||||
|
|
@ -202,112 +254,63 @@ guardian_print (SCM guardian, SCM port, scm_print_state *pstate SCM_UNUSED)
|
||||||
static SCM
|
static SCM
|
||||||
guardian_apply (SCM guardian, SCM obj, SCM throw_p)
|
guardian_apply (SCM guardian, SCM obj, SCM throw_p)
|
||||||
{
|
{
|
||||||
if (DESTROYED_P (GUARDIAN_DATA (guardian)))
|
#if ENABLE_DEPRECATED
|
||||||
scm_misc_error ("guard", "attempted use of destroyed guardian: ~A",
|
if (!SCM_UNBNDP (throw_p))
|
||||||
scm_list_1 (guardian));
|
scm_c_issue_deprecation_warning
|
||||||
|
("Using the 'throw?' argument of a guardian is deprecated "
|
||||||
|
"and ineffective.");
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!SCM_UNBNDP (obj))
|
if (!SCM_UNBNDP (obj))
|
||||||
return scm_guard (guardian, obj,
|
|
||||||
(SCM_UNBNDP (throw_p)
|
|
||||||
? 1
|
|
||||||
: scm_is_true (throw_p)));
|
|
||||||
else
|
|
||||||
return scm_get_one_zombie (guardian);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SCM
|
|
||||||
scm_guard (SCM guardian, SCM obj, int throw_p)
|
|
||||||
{
|
|
||||||
t_guardian *g = GUARDIAN_DATA (guardian);
|
|
||||||
|
|
||||||
if (!SCM_IMP (obj))
|
|
||||||
{
|
{
|
||||||
SCM z;
|
scm_i_guard (guardian, obj);
|
||||||
|
return SCM_UNSPECIFIED;
|
||||||
/* This critical section barrier will be replaced by a mutex. */
|
|
||||||
/* njrev: per comment above, should use a mutex. */
|
|
||||||
SCM_CRITICAL_SECTION_START;
|
|
||||||
|
|
||||||
if (GREEDY_P (g))
|
|
||||||
{
|
|
||||||
if (scm_is_true (scm_hashq_get_handle
|
|
||||||
(greedily_guarded_whash, obj)))
|
|
||||||
{
|
|
||||||
SCM_CRITICAL_SECTION_END;
|
|
||||||
|
|
||||||
if (throw_p)
|
|
||||||
scm_misc_error ("guard",
|
|
||||||
"object is already greedily guarded: ~A",
|
|
||||||
scm_list_1 (obj));
|
|
||||||
else
|
|
||||||
return SCM_BOOL_F;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scm_hashq_create_handle_x (greedily_guarded_whash,
|
|
||||||
obj, guardian);
|
|
||||||
/* njrev: this can throw a memory or out-of-range error. */
|
|
||||||
}
|
|
||||||
|
|
||||||
z = scm_cons (SCM_BOOL_F, SCM_BOOL_F);
|
|
||||||
TCONC_IN (g->live, obj, z);
|
|
||||||
|
|
||||||
SCM_CRITICAL_SECTION_END;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return throw_p ? SCM_UNSPECIFIED : SCM_BOOL_T;
|
return scm_i_get_one_zombie (guardian);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCM_DEFINE (scm_make_guardian, "make-guardian", 0, 0, 0,
|
||||||
SCM
|
(),
|
||||||
scm_get_one_zombie (SCM guardian)
|
"Create a new guardian. A guardian protects a set of objects from\n"
|
||||||
{
|
"garbage collection, allowing a program to apply cleanup or other\n"
|
||||||
t_guardian *g = GUARDIAN_DATA (guardian);
|
"actions.\n"
|
||||||
SCM res = SCM_BOOL_F;
|
"\n"
|
||||||
|
"@code{make-guardian} returns a procedure representing the guardian.\n"
|
||||||
/* This critical section barrier will be replaced by a mutex. */
|
"Calling the guardian procedure with an argument adds the argument to\n"
|
||||||
SCM_CRITICAL_SECTION_START;
|
"the guardian's set of protected objects. Calling the guardian\n"
|
||||||
/* njrev: -> mutex */
|
"procedure without an argument returns one of the protected objects\n"
|
||||||
|
"which are ready for garbage collection, or @code{#f} if no such object\n"
|
||||||
if (!TCONC_EMPTYP (g->zombies))
|
"is available. Objects which are returned in this way are removed from\n"
|
||||||
TCONC_OUT (g->zombies, res);
|
"the guardian.\n"
|
||||||
|
"\n"
|
||||||
if (scm_is_true (res) && GREEDY_P (g))
|
"You can put a single object into a guardian more than once and you can\n"
|
||||||
scm_hashq_remove_x (greedily_guarded_whash, res);
|
"put a single object into more than one guardian. The object will then\n"
|
||||||
|
"be returned multiple times by the guardian procedures.\n"
|
||||||
SCM_CRITICAL_SECTION_END;
|
"\n"
|
||||||
|
"An object is eligible to be returned from a guardian when it is no\n"
|
||||||
return res;
|
"longer referenced from outside any guardian.\n"
|
||||||
}
|
"\n"
|
||||||
|
"There is no guarantee about the order in which objects are returned\n"
|
||||||
|
"from a guardian. If you want to impose an order on finalization\n"
|
||||||
SCM_DEFINE (scm_make_guardian, "make-guardian", 0, 1, 0,
|
"actions, for example, you can do that by keeping objects alive in some\n"
|
||||||
(SCM greedy_p),
|
"global data structure until they are no longer needed for finalizing\n"
|
||||||
"Create a new guardian.\n"
|
"other objects.\n"
|
||||||
"A guardian protects a set of objects from garbage collection,\n"
|
"\n"
|
||||||
"allowing a program to apply cleanup or other actions.\n\n"
|
"Being an element in a weak vector, a key in a hash table with weak\n"
|
||||||
|
"keys, or a value in a hash table with weak value does not prevent an\n"
|
||||||
"@code{make-guardian} returns a procedure representing the guardian.\n"
|
"object from being returned by a guardian. But as long as an object\n"
|
||||||
"Calling the guardian procedure with an argument adds the\n"
|
"can be returned from a guardian it will not be removed from such a\n"
|
||||||
"argument to the guardian's set of protected objects.\n"
|
"weak vector or hash table. In other words, a weak link does not\n"
|
||||||
"Calling the guardian procedure without an argument returns\n"
|
"prevent an object from being considered collectable, but being inside\n"
|
||||||
"one of the protected objects which are ready for garbage\n"
|
"a guardian prevents a weak link from being broken.\n"
|
||||||
"collection, or @code{#f} if no such object is available.\n"
|
"\n"
|
||||||
"Objects which are returned in this way are removed from\n"
|
"A key in a weak key hash table can be though of as having a strong\n"
|
||||||
"the guardian.\n\n"
|
"reference to its associated value as long as the key is accessible.\n"
|
||||||
|
"Consequently, when the key only accessible from within a guardian, the\n"
|
||||||
"@code{make-guardian} takes one optional argument that says whether the\n"
|
"reference from the key to the value is also considered to be coming\n"
|
||||||
"new guardian should be greedy or sharing. If there is any chance\n"
|
"from within a guardian. Thus, if there is no other reference to the\n"
|
||||||
"that any object protected by the guardian may be resurrected,\n"
|
"value, it is eligible to be returned from a guardian.\n")
|
||||||
"then you should make the guardian greedy (this is the default).\n\n"
|
|
||||||
|
|
||||||
"See R. Kent Dybvig, Carl Bruggeman, and David Eby (1993)\n"
|
|
||||||
"\"Guardians in a Generation-Based Garbage Collector\".\n"
|
|
||||||
"ACM SIGPLAN Conference on Programming Language Design\n"
|
|
||||||
"and Implementation, June 1993.\n\n"
|
|
||||||
|
|
||||||
"(the semantics are slightly different at this point, but the\n"
|
|
||||||
"paper still (mostly) accurately describes the interface).")
|
|
||||||
#define FUNC_NAME s_scm_make_guardian
|
#define FUNC_NAME s_scm_make_guardian
|
||||||
{
|
{
|
||||||
t_guardian *g = scm_gc_malloc (sizeof (t_guardian), "guardian");
|
t_guardian *g = scm_gc_malloc (sizeof (t_guardian), "guardian");
|
||||||
|
|
@ -320,280 +323,13 @@ SCM_DEFINE (scm_make_guardian, "make-guardian", 0, 1, 0,
|
||||||
g->zombies.head = g->zombies.tail = z2;
|
g->zombies.head = g->zombies.tail = z2;
|
||||||
|
|
||||||
g->next = NULL;
|
g->next = NULL;
|
||||||
g->flags = 0L;
|
|
||||||
|
|
||||||
/* [cmm] the UNBNDP check below is redundant but I like it. */
|
|
||||||
if (SCM_UNBNDP (greedy_p) || scm_is_true (greedy_p))
|
|
||||||
SET_GREEDY (g);
|
|
||||||
|
|
||||||
SCM_NEWSMOB (z, tc16_guardian, g);
|
SCM_NEWSMOB (z, tc16_guardian, g);
|
||||||
|
|
||||||
return z;
|
return z;
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
SCM_DEFINE (scm_guardian_destroyed_p, "guardian-destroyed?", 1, 0, 0,
|
|
||||||
(SCM guardian),
|
|
||||||
"Return @code{#t} if @var{guardian} has been destroyed, otherwise @code{#f}.")
|
|
||||||
#define FUNC_NAME s_scm_guardian_destroyed_p
|
|
||||||
{
|
|
||||||
SCM res = SCM_BOOL_F;
|
|
||||||
|
|
||||||
/* This critical section barrier will be replaced by a mutex. */
|
|
||||||
SCM_CRITICAL_SECTION_START;
|
|
||||||
/* njrev: Critical section not needed here. (Falls into category of
|
|
||||||
stuff that is the responsibility of Scheme code, whenever
|
|
||||||
accessing data from multiple threads.) */
|
|
||||||
res = scm_from_bool (DESTROYED_P (GUARDIAN_DATA (guardian)));
|
|
||||||
|
|
||||||
SCM_CRITICAL_SECTION_END;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
#undef FUNC_NAME
|
|
||||||
|
|
||||||
SCM_DEFINE (scm_guardian_greedy_p, "guardian-greedy?", 1, 0, 0,
|
|
||||||
(SCM guardian),
|
|
||||||
"Return @code{#t} if @var{guardian} is a greedy guardian, otherwise @code{#f}.")
|
|
||||||
#define FUNC_NAME s_scm_guardian_greedy_p
|
|
||||||
{
|
|
||||||
return scm_from_bool (GREEDY_P (GUARDIAN_DATA (guardian)));
|
|
||||||
}
|
|
||||||
#undef FUNC_NAME
|
|
||||||
|
|
||||||
SCM_DEFINE (scm_destroy_guardian_x, "destroy-guardian!", 1, 0, 0,
|
|
||||||
(SCM guardian),
|
|
||||||
"Destroys @var{guardian}, by making it impossible to put any more\n"
|
|
||||||
"objects in it or get any objects from it. It also unguards any\n"
|
|
||||||
"objects guarded by @var{guardian}.")
|
|
||||||
#define FUNC_NAME s_scm_destroy_guardian_x
|
|
||||||
{
|
|
||||||
t_guardian *g = GUARDIAN_DATA (guardian);
|
|
||||||
|
|
||||||
/* This critical section barrier will be replaced by a mutex. */
|
|
||||||
SCM_CRITICAL_SECTION_START;
|
|
||||||
|
|
||||||
if (DESTROYED_P (g))
|
|
||||||
{
|
|
||||||
SCM_CRITICAL_SECTION_END;
|
|
||||||
SCM_MISC_ERROR ("guardian is already destroyed: ~A",
|
|
||||||
scm_list_1 (guardian));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GREEDY_P (g))
|
|
||||||
{
|
|
||||||
/* clear the "greedily guarded" property of the objects */
|
|
||||||
SCM pair;
|
|
||||||
for (pair = g->live.head; pair != g->live.tail; pair = SCM_CDR (pair))
|
|
||||||
scm_hashq_remove_x (greedily_guarded_whash, SCM_CAR (pair));
|
|
||||||
for (pair = g->zombies.head; pair != g->zombies.tail; pair = SCM_CDR (pair))
|
|
||||||
scm_hashq_remove_x (greedily_guarded_whash, SCM_CAR (pair));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* empty the lists */
|
|
||||||
g->live.head = g->live.tail;
|
|
||||||
g->zombies.head = g->zombies.tail;
|
|
||||||
|
|
||||||
SET_DESTROYED (g);
|
|
||||||
|
|
||||||
SCM_CRITICAL_SECTION_END;
|
|
||||||
|
|
||||||
return SCM_UNSPECIFIED;
|
|
||||||
}
|
|
||||||
#undef FUNC_NAME
|
|
||||||
|
|
||||||
/* called before gc mark phase begins to initialise the live guardian list. */
|
|
||||||
static void *
|
|
||||||
guardian_gc_init (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
|
||||||
void *dummy3 SCM_UNUSED)
|
|
||||||
{
|
|
||||||
greedy_guardians = sharing_guardians = NULL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mark_dependencies_in_tconc (t_tconc *tc)
|
|
||||||
{
|
|
||||||
SCM pair, next_pair;
|
|
||||||
SCM *prev_ptr;
|
|
||||||
|
|
||||||
/* scan the list for unmarked objects, and mark their
|
|
||||||
dependencies */
|
|
||||||
for (pair = tc->head, prev_ptr = &tc->head;
|
|
||||||
!scm_is_eq (pair, tc->tail);
|
|
||||||
pair = next_pair)
|
|
||||||
{
|
|
||||||
SCM obj = SCM_CAR (pair);
|
|
||||||
next_pair = SCM_CDR (pair);
|
|
||||||
|
|
||||||
if (! SCM_GC_MARK_P (obj))
|
|
||||||
{
|
|
||||||
/* a candidate for finalizing */
|
|
||||||
scm_gc_mark_dependencies (obj);
|
|
||||||
|
|
||||||
if (SCM_GC_MARK_P (obj))
|
|
||||||
{
|
|
||||||
/* uh oh. a cycle. transfer this object (the
|
|
||||||
spine cell, to be exact) to
|
|
||||||
self_centered_zombies, so we'll be able to
|
|
||||||
complain about it later. */
|
|
||||||
*prev_ptr = next_pair;
|
|
||||||
SCM_SET_GC_MARK (pair);
|
|
||||||
SCM_SETCDR (pair, self_centered_zombies);
|
|
||||||
self_centered_zombies = pair;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* see if this is a guardian. if yes, list it (but don't
|
|
||||||
mark it yet). */
|
|
||||||
if (GUARDIAN_P (obj))
|
|
||||||
add_to_live_list (GUARDIAN_DATA (obj));
|
|
||||||
|
|
||||||
prev_ptr = SCM_CDRLOC (pair);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mark_dependencies (t_guardian *g)
|
|
||||||
{
|
|
||||||
mark_dependencies_in_tconc (&g->zombies);
|
|
||||||
mark_dependencies_in_tconc (&g->live);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
|
||||||
mark_and_zombify (t_guardian *g)
|
|
||||||
{
|
|
||||||
SCM tconc_tail = g->live.tail;
|
|
||||||
SCM *prev_ptr = &g->live.head;
|
|
||||||
SCM pair = g->live.head;
|
|
||||||
|
|
||||||
while (!scm_is_eq (pair, tconc_tail))
|
|
||||||
{
|
|
||||||
SCM next_pair = SCM_CDR (pair);
|
|
||||||
|
|
||||||
if (!SCM_GC_MARK_P (SCM_CAR (pair)))
|
|
||||||
{
|
|
||||||
/* got you, zombie! */
|
|
||||||
|
|
||||||
/* out of the live list! */
|
|
||||||
*prev_ptr = next_pair;
|
|
||||||
|
|
||||||
if (GREEDY_P (g))
|
|
||||||
/* if the guardian is greedy, mark this zombie now. this
|
|
||||||
way it won't be zombified again this time around. */
|
|
||||||
SCM_SET_GC_MARK (SCM_CAR (pair));
|
|
||||||
|
|
||||||
/* into the zombie list! */
|
|
||||||
TCONC_IN (g->zombies, SCM_CAR (pair), pair);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
prev_ptr = SCM_CDRLOC (pair);
|
|
||||||
|
|
||||||
pair = next_pair;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Mark the cells of the live list (yes, the cells in the list, we
|
|
||||||
don't care about objects pointed to by the list cars, since we
|
|
||||||
know they are already marked). */
|
|
||||||
for (pair = g->live.head; !scm_is_null (pair); pair = SCM_CDR (pair))
|
|
||||||
SCM_SET_GC_MARK (pair);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* this is called by the garbage collector between the mark and sweep
|
|
||||||
phases. for each marked guardian, it moves any unmarked object in
|
|
||||||
its live list (tconc) to its zombie list (tconc). */
|
|
||||||
static void *
|
|
||||||
guardian_zombify (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
|
||||||
void *dummy3 SCM_UNUSED)
|
|
||||||
{
|
|
||||||
t_guardian *last_greedy_guardian = NULL;
|
|
||||||
t_guardian *last_sharing_guardian = NULL;
|
|
||||||
t_guardian *first_greedy_guardian = NULL;
|
|
||||||
t_guardian *first_sharing_guardian = NULL;
|
|
||||||
t_guardian *g;
|
|
||||||
|
|
||||||
/* First, find all newly unreachable objects and mark their
|
|
||||||
dependencies.
|
|
||||||
|
|
||||||
Note that new guardians may be stuck on the end of the live
|
|
||||||
guardian lists as we run this loop, since guardians might be
|
|
||||||
guarded too. When we mark a guarded guardian, its mark function
|
|
||||||
sticks in the appropriate live guardian list. The loop
|
|
||||||
terminates when no new guardians are found. */
|
|
||||||
|
|
||||||
do {
|
|
||||||
first_greedy_guardian = greedy_guardians;
|
|
||||||
first_sharing_guardian = sharing_guardians;
|
|
||||||
|
|
||||||
for (g = greedy_guardians; g != last_greedy_guardian;
|
|
||||||
g = g->next)
|
|
||||||
mark_dependencies (g);
|
|
||||||
for (g = sharing_guardians; g != last_sharing_guardian;
|
|
||||||
g = g->next)
|
|
||||||
mark_dependencies (g);
|
|
||||||
|
|
||||||
last_greedy_guardian = first_greedy_guardian;
|
|
||||||
last_sharing_guardian = first_sharing_guardian;
|
|
||||||
} while (first_greedy_guardian != greedy_guardians
|
|
||||||
|| first_sharing_guardian != sharing_guardians);
|
|
||||||
|
|
||||||
/* now, scan all the guardians that are currently known to be live
|
|
||||||
and move their unmarked objects to zombie lists. */
|
|
||||||
|
|
||||||
for (g = greedy_guardians; g; g = g->next)
|
|
||||||
{
|
|
||||||
mark_and_zombify (g);
|
|
||||||
CLR_LISTED (g);
|
|
||||||
}
|
|
||||||
for (g = sharing_guardians; g; g = g->next)
|
|
||||||
{
|
|
||||||
mark_and_zombify (g);
|
|
||||||
CLR_LISTED (g);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Preserve the zombies in their undead state, by marking to prevent
|
|
||||||
collection. */
|
|
||||||
for (g = greedy_guardians; g; g = g->next)
|
|
||||||
scm_gc_mark (g->zombies.head);
|
|
||||||
for (g = sharing_guardians; g; g = g->next)
|
|
||||||
scm_gc_mark (g->zombies.head);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *
|
|
||||||
whine_about_self_centered_zombies (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
|
||||||
void *dummy3 SCM_UNUSED)
|
|
||||||
{
|
|
||||||
if (!scm_is_null (self_centered_zombies))
|
|
||||||
{
|
|
||||||
SCM port = scm_current_error_port ();
|
|
||||||
SCM pair;
|
|
||||||
|
|
||||||
scm_puts ("** WARNING: the following guarded objects were unguarded due to cycles:",
|
|
||||||
port);
|
|
||||||
scm_newline (port);
|
|
||||||
for (pair = self_centered_zombies;
|
|
||||||
!scm_is_null (pair); pair = SCM_CDR (pair))
|
|
||||||
{
|
|
||||||
scm_display (SCM_CAR (pair), port);
|
|
||||||
scm_newline (port);
|
|
||||||
}
|
|
||||||
|
|
||||||
self_centered_zombies = SCM_EOL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
scm_init_guardians ()
|
scm_init_guardians ()
|
||||||
{
|
{
|
||||||
|
|
@ -601,17 +337,11 @@ scm_init_guardians ()
|
||||||
scm_set_smob_mark (tc16_guardian, guardian_mark);
|
scm_set_smob_mark (tc16_guardian, guardian_mark);
|
||||||
scm_set_smob_free (tc16_guardian, guardian_free);
|
scm_set_smob_free (tc16_guardian, guardian_free);
|
||||||
scm_set_smob_print (tc16_guardian, guardian_print);
|
scm_set_smob_print (tc16_guardian, guardian_print);
|
||||||
|
#if ENABLE_DEPRECATED
|
||||||
scm_set_smob_apply (tc16_guardian, guardian_apply, 0, 2, 0);
|
scm_set_smob_apply (tc16_guardian, guardian_apply, 0, 2, 0);
|
||||||
|
#else
|
||||||
scm_c_hook_add (&scm_before_mark_c_hook, guardian_gc_init, 0, 0);
|
scm_set_smob_apply (tc16_guardian, guardian_apply, 0, 1, 0);
|
||||||
scm_c_hook_add (&scm_before_sweep_c_hook, guardian_zombify, 0, 0);
|
#endif
|
||||||
|
|
||||||
scm_gc_register_root (&self_centered_zombies);
|
|
||||||
scm_c_hook_add (&scm_after_gc_c_hook,
|
|
||||||
whine_about_self_centered_zombies, 0, 0);
|
|
||||||
|
|
||||||
greedily_guarded_whash =
|
|
||||||
scm_permanent_object (scm_make_doubly_weak_hash_table (scm_from_int (31)));
|
|
||||||
|
|
||||||
#include "libguile/guardians.x"
|
#include "libguile/guardians.x"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,11 @@
|
||||||
|
|
||||||
#include "libguile/__scm.h"
|
#include "libguile/__scm.h"
|
||||||
|
|
||||||
SCM_API SCM scm_make_guardian (SCM greedy_p);
|
SCM_API SCM scm_make_guardian (void);
|
||||||
SCM_API SCM scm_destroy_guardian_x (SCM guardian);
|
|
||||||
|
|
||||||
SCM_API SCM scm_guardian_greedy_p (SCM guardian);
|
SCM_API void scm_i_init_guardians_for_gc (void);
|
||||||
SCM_API SCM scm_guardian_destroyed_p (SCM guardian);
|
SCM_API void scm_i_identify_inaccessible_guardeds (void);
|
||||||
|
SCM_API int scm_i_mark_inaccessible_guardeds (void);
|
||||||
/* these are to be called from C: */
|
|
||||||
SCM_API SCM scm_guard (SCM guardian, SCM obj, int throw_p);
|
|
||||||
SCM_API SCM scm_get_one_zombie (SCM guardian);
|
|
||||||
|
|
||||||
SCM_API void scm_init_guardians (void);
|
SCM_API void scm_init_guardians (void);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "libguile/_scm.h"
|
#include "libguile/_scm.h"
|
||||||
#include "libguile/alist.h"
|
#include "libguile/alist.h"
|
||||||
#include "libguile/hash.h"
|
#include "libguile/hash.h"
|
||||||
|
|
@ -88,12 +90,7 @@ make_hash_table (int flags, unsigned long k, const char *func_name)
|
||||||
++i;
|
++i;
|
||||||
n = hashtable_size[i];
|
n = hashtable_size[i];
|
||||||
if (flags)
|
if (flags)
|
||||||
/* The SCM_WVECTF_NOSCAN flag informs the weak vector code not to
|
vector = scm_i_allocate_weak_vector (flags, scm_from_int (n), SCM_EOL);
|
||||||
perform the final scan for broken references. Instead we do
|
|
||||||
that ourselves in scan_weak_hashtables. */
|
|
||||||
vector = scm_i_allocate_weak_vector (flags | SCM_WVECTF_NOSCAN,
|
|
||||||
scm_from_int (n),
|
|
||||||
SCM_EOL);
|
|
||||||
else
|
else
|
||||||
vector = scm_c_make_vector (n, SCM_EOL);
|
vector = scm_c_make_vector (n, SCM_EOL);
|
||||||
t = scm_gc_malloc (sizeof (*t), s_hashtable);
|
t = scm_gc_malloc (sizeof (*t), s_hashtable);
|
||||||
|
|
@ -158,8 +155,7 @@ scm_i_rehash (SCM table,
|
||||||
buckets = SCM_HASHTABLE_VECTOR (table);
|
buckets = SCM_HASHTABLE_VECTOR (table);
|
||||||
|
|
||||||
if (SCM_HASHTABLE_WEAK_P (table))
|
if (SCM_HASHTABLE_WEAK_P (table))
|
||||||
new_buckets = scm_i_allocate_weak_vector (SCM_HASHTABLE_FLAGS (table)
|
new_buckets = scm_i_allocate_weak_vector (SCM_HASHTABLE_FLAGS (table),
|
||||||
| SCM_WVECTF_NOSCAN,
|
|
||||||
scm_from_ulong (new_size),
|
scm_from_ulong (new_size),
|
||||||
SCM_EOL);
|
SCM_EOL);
|
||||||
else
|
else
|
||||||
|
|
@ -167,16 +163,13 @@ scm_i_rehash (SCM table,
|
||||||
|
|
||||||
/* When this is a weak hashtable, running the GC might change it.
|
/* When this is a weak hashtable, running the GC might change it.
|
||||||
We need to cope with this while rehashing its elements. We do
|
We need to cope with this while rehashing its elements. We do
|
||||||
this by first installing the new, empty bucket vector and turning
|
this by first installing the new, empty bucket vector. Then we
|
||||||
the old bucket vector into a regularily scanned weak vector.
|
remove the elements from the old bucket vector and insert them
|
||||||
Then we remove the elements from the old bucket vector and insert
|
into the new one.
|
||||||
them into the new one.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
|
SCM_SET_HASHTABLE_VECTOR (table, new_buckets);
|
||||||
SCM_SET_HASHTABLE_N_ITEMS (table, 0);
|
SCM_SET_HASHTABLE_N_ITEMS (table, 0);
|
||||||
if (SCM_HASHTABLE_WEAK_P (table))
|
|
||||||
SCM_I_SET_WVECT_TYPE (buckets, ((scm_t_bits) SCM_HASHTABLE_FLAGS (table)));
|
|
||||||
|
|
||||||
old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
|
old_size = SCM_SIMPLE_VECTOR_LENGTH (buckets);
|
||||||
for (i = 0; i < old_size; ++i)
|
for (i = 0; i < old_size; ++i)
|
||||||
|
|
@ -206,7 +199,6 @@ scm_i_rehash (SCM table,
|
||||||
static int
|
static int
|
||||||
hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
|
hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
|
||||||
{
|
{
|
||||||
scm_t_hashtable *t = SCM_HASHTABLE (exp);
|
|
||||||
scm_puts ("#<", port);
|
scm_puts ("#<", port);
|
||||||
if (SCM_HASHTABLE_WEAK_KEY_P (exp))
|
if (SCM_HASHTABLE_WEAK_KEY_P (exp))
|
||||||
scm_puts ("weak-key-", port);
|
scm_puts ("weak-key-", port);
|
||||||
|
|
@ -215,7 +207,7 @@ hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
|
||||||
else if (SCM_HASHTABLE_DOUBLY_WEAK_P (exp))
|
else if (SCM_HASHTABLE_DOUBLY_WEAK_P (exp))
|
||||||
scm_puts ("doubly-weak-", port);
|
scm_puts ("doubly-weak-", port);
|
||||||
scm_puts ("hash-table ", port);
|
scm_puts ("hash-table ", port);
|
||||||
scm_uintprint (t->n_items, 10, port);
|
scm_uintprint (SCM_HASHTABLE_N_ITEMS (exp), 10, port);
|
||||||
scm_putc ('/', port);
|
scm_putc ('/', port);
|
||||||
scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
|
scm_uintprint (SCM_SIMPLE_VECTOR_LENGTH (SCM_HASHTABLE_VECTOR (exp)),
|
||||||
10, port);
|
10, port);
|
||||||
|
|
@ -228,12 +220,9 @@ hashtable_print (SCM exp, SCM port, scm_print_state *pstate SCM_UNUSED)
|
||||||
/* keep track of hash tables that need to shrink after scan */
|
/* keep track of hash tables that need to shrink after scan */
|
||||||
static SCM to_rehash = SCM_EOL;
|
static SCM to_rehash = SCM_EOL;
|
||||||
|
|
||||||
/* scan hash tables for broken references, remove them, and update
|
/* scan hash tables and update hash tables item count */
|
||||||
hash tables item count */
|
void
|
||||||
static void *
|
scm_i_scan_weak_hashtables ()
|
||||||
scan_weak_hashtables (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
|
||||||
void *dummy3 SCM_UNUSED)
|
|
||||||
{
|
{
|
||||||
SCM *next = &weak_hashtables;
|
SCM *next = &weak_hashtables;
|
||||||
SCM h = *next;
|
SCM h = *next;
|
||||||
|
|
@ -243,34 +232,12 @@ scan_weak_hashtables (void *dummy1 SCM_UNUSED,
|
||||||
*next = h = SCM_HASHTABLE_NEXT (h);
|
*next = h = SCM_HASHTABLE_NEXT (h);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SCM alist;
|
SCM vec = SCM_HASHTABLE_VECTOR (h);
|
||||||
int i, n = SCM_HASHTABLE_N_BUCKETS (h);
|
size_t delta = SCM_I_WVECT_DELTA (vec);
|
||||||
int weak_car = SCM_HASHTABLE_FLAGS (h) & SCM_HASHTABLEF_WEAK_CAR;
|
SCM_I_SET_WVECT_DELTA (vec, 0);
|
||||||
int weak_cdr = SCM_HASHTABLE_FLAGS (h) & SCM_HASHTABLEF_WEAK_CDR;
|
SCM_SET_HASHTABLE_N_ITEMS (h, SCM_HASHTABLE_N_ITEMS (h) - delta);
|
||||||
int check_size_p = 0;
|
|
||||||
for (i = 0; i < n; ++i)
|
if (SCM_HASHTABLE_N_ITEMS (h) < SCM_HASHTABLE_LOWER (h))
|
||||||
{
|
|
||||||
SCM *next_spine = NULL;
|
|
||||||
alist = SCM_HASHTABLE_BUCKET (h, i);
|
|
||||||
while (scm_is_pair (alist))
|
|
||||||
{
|
|
||||||
if ((weak_car && UNMARKED_CELL_P (SCM_CAAR (alist)))
|
|
||||||
|| (weak_cdr && UNMARKED_CELL_P (SCM_CDAR (alist))))
|
|
||||||
{
|
|
||||||
if (next_spine)
|
|
||||||
*next_spine = SCM_CDR (alist);
|
|
||||||
else
|
|
||||||
SCM_SET_HASHTABLE_BUCKET (h, i, SCM_CDR (alist));
|
|
||||||
SCM_HASHTABLE_DECREMENT (h);
|
|
||||||
check_size_p = 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
next_spine = SCM_CDRLOC (alist);
|
|
||||||
alist = SCM_CDR (alist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (check_size_p
|
|
||||||
&& SCM_HASHTABLE_N_ITEMS (h) < SCM_HASHTABLE_LOWER (h))
|
|
||||||
{
|
{
|
||||||
SCM tmp = SCM_HASHTABLE_NEXT (h);
|
SCM tmp = SCM_HASHTABLE_NEXT (h);
|
||||||
/* temporarily move table from weak_hashtables to to_rehash */
|
/* temporarily move table from weak_hashtables to to_rehash */
|
||||||
|
|
@ -285,7 +252,6 @@ scan_weak_hashtables (void *dummy1 SCM_UNUSED,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *
|
static void *
|
||||||
|
|
@ -1104,7 +1070,6 @@ scm_hashtab_prehistory ()
|
||||||
scm_set_smob_mark (scm_tc16_hashtable, scm_markcdr);
|
scm_set_smob_mark (scm_tc16_hashtable, scm_markcdr);
|
||||||
scm_set_smob_print (scm_tc16_hashtable, hashtable_print);
|
scm_set_smob_print (scm_tc16_hashtable, hashtable_print);
|
||||||
scm_set_smob_free (scm_tc16_hashtable, hashtable_free);
|
scm_set_smob_free (scm_tc16_hashtable, hashtable_free);
|
||||||
scm_c_hook_add (&scm_after_sweep_c_hook, scan_weak_hashtables, 0, 0);
|
|
||||||
scm_c_hook_add (&scm_after_gc_c_hook, rehash_after_gc, 0, 0);
|
scm_c_hook_add (&scm_after_gc_c_hook, rehash_after_gc, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ SCM_API SCM scm_weak_value_hash_table_p (SCM h);
|
||||||
SCM_API SCM scm_doubly_weak_hash_table_p (SCM h);
|
SCM_API SCM scm_doubly_weak_hash_table_p (SCM h);
|
||||||
|
|
||||||
SCM_API void scm_i_rehash (SCM table, unsigned long (*hash_fn)(), void *closure, const char*func_name);
|
SCM_API void scm_i_rehash (SCM table, unsigned long (*hash_fn)(), void *closure, const char*func_name);
|
||||||
|
SCM_API void scm_i_scan_weak_hashtables (void);
|
||||||
|
|
||||||
SCM_API SCM scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn) (), SCM (*assoc_fn) (), void * closure);
|
SCM_API SCM scm_hash_fn_get_handle (SCM table, SCM obj, unsigned long (*hash_fn) (), SCM (*assoc_fn) (), void * closure);
|
||||||
SCM_API SCM scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn) (), SCM (*assoc_fn) (), void * closure);
|
SCM_API SCM scm_hash_fn_create_handle_x (SCM table, SCM obj, SCM init, unsigned long (*hash_fn) (), SCM (*assoc_fn) (), void * closure);
|
||||||
|
|
|
||||||
|
|
@ -428,7 +428,6 @@ scm_i_init_guile (SCM_STACKITEM *base)
|
||||||
|
|
||||||
scm_struct_prehistory (); /* requires storage */
|
scm_struct_prehistory (); /* requires storage */
|
||||||
scm_symbols_prehistory (); /* requires storage */
|
scm_symbols_prehistory (); /* requires storage */
|
||||||
scm_weaks_prehistory (); /* requires storage */
|
|
||||||
scm_init_subr_table ();
|
scm_init_subr_table ();
|
||||||
scm_environments_prehistory (); /* requires storage */
|
scm_environments_prehistory (); /* requires storage */
|
||||||
scm_modules_prehistory (); /* requires storage and hash tables */
|
scm_modules_prehistory (); /* requires storage and hash tables */
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,8 @@ SCM_API SCM scm_i_vector_equal_p (SCM x, SCM y);
|
||||||
#define SCM_I_WVECT_LENGTH SCM_I_VECTOR_LENGTH
|
#define SCM_I_WVECT_LENGTH SCM_I_VECTOR_LENGTH
|
||||||
#define SCM_I_WVECT_VELTS SCM_I_VECTOR_ELTS
|
#define SCM_I_WVECT_VELTS SCM_I_VECTOR_ELTS
|
||||||
#define SCM_I_WVECT_GC_WVELTS SCM_I_VECTOR_WELTS
|
#define SCM_I_WVECT_GC_WVELTS SCM_I_VECTOR_WELTS
|
||||||
#define SCM_I_WVECT_TYPE(x) (SCM_CELL_WORD_2 (x))
|
#define SCM_I_WVECT_EXTRA(x) (SCM_CELL_WORD_2 (x))
|
||||||
#define SCM_I_SET_WVECT_TYPE(x, t) (SCM_SET_CELL_WORD_2 ((x),(t)))
|
#define SCM_I_SET_WVECT_EXTRA(x, t) (SCM_SET_CELL_WORD_2 ((x),(t)))
|
||||||
#define SCM_I_WVECT_GC_CHAIN(x) (SCM_CELL_OBJECT_3 (x))
|
#define SCM_I_WVECT_GC_CHAIN(x) (SCM_CELL_OBJECT_3 (x))
|
||||||
#define SCM_I_SET_WVECT_GC_CHAIN(x, o) (SCM_SET_CELL_OBJECT_3 ((x), (o)))
|
#define SCM_I_SET_WVECT_GC_CHAIN(x, o) (SCM_SET_CELL_OBJECT_3 ((x), (o)))
|
||||||
|
|
||||||
|
|
|
||||||
256
libguile/weaks.c
256
libguile/weaks.c
|
|
@ -42,6 +42,8 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "libguile/_scm.h"
|
#include "libguile/_scm.h"
|
||||||
#include "libguile/vectors.h"
|
#include "libguile/vectors.h"
|
||||||
#include "libguile/lang.h"
|
#include "libguile/lang.h"
|
||||||
|
|
@ -201,132 +203,180 @@ SCM_DEFINE (scm_doubly_weak_alist_vector_p, "doubly-weak-alist-vector?", 1, 0, 0
|
||||||
}
|
}
|
||||||
#undef FUNC_NAME
|
#undef FUNC_NAME
|
||||||
|
|
||||||
|
|
||||||
static void *
|
|
||||||
scm_weak_vector_gc_init (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
|
||||||
void *dummy3 SCM_UNUSED)
|
|
||||||
{
|
|
||||||
scm_weak_vectors = SCM_EOL;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static void *
|
|
||||||
scm_mark_weak_vector_spines (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
|
||||||
void *dummy3 SCM_UNUSED)
|
|
||||||
{
|
|
||||||
SCM w;
|
|
||||||
|
|
||||||
for (w = scm_weak_vectors; !scm_is_null (w); w = SCM_I_WVECT_GC_CHAIN (w))
|
|
||||||
{
|
|
||||||
if (SCM_IS_WHVEC_ANY (w))
|
|
||||||
{
|
|
||||||
SCM const *ptr;
|
|
||||||
SCM obj;
|
|
||||||
long j;
|
|
||||||
long n;
|
|
||||||
|
|
||||||
obj = w;
|
|
||||||
ptr = SCM_I_WVECT_GC_WVELTS (w);
|
|
||||||
n = SCM_I_WVECT_LENGTH (w);
|
|
||||||
for (j = 0; j < n; ++j)
|
|
||||||
{
|
|
||||||
SCM alist;
|
|
||||||
|
|
||||||
alist = ptr[j];
|
|
||||||
while ( scm_is_pair (alist)
|
|
||||||
&& !SCM_GC_MARK_P (alist)
|
|
||||||
&& scm_is_pair (SCM_CAR (alist)))
|
|
||||||
{
|
|
||||||
SCM_SET_GC_MARK (alist);
|
|
||||||
SCM_SET_GC_MARK (SCM_CAR (alist));
|
|
||||||
alist = SCM_CDR (alist);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define UNMARKED_CELL_P(x) (SCM_NIMP(x) && !SCM_GC_MARK_P (x))
|
#define UNMARKED_CELL_P(x) (SCM_NIMP(x) && !SCM_GC_MARK_P (x))
|
||||||
|
|
||||||
static void *
|
static SCM weak_vectors;
|
||||||
scm_scan_weak_vectors (void *dummy1 SCM_UNUSED,
|
|
||||||
void *dummy2 SCM_UNUSED,
|
void
|
||||||
void *dummy3 SCM_UNUSED)
|
scm_i_init_weak_vectors_for_gc ()
|
||||||
{
|
{
|
||||||
SCM *ptr, w;
|
weak_vectors = SCM_EOL;
|
||||||
for (w = scm_weak_vectors; !scm_is_null (w); w = SCM_I_WVECT_GC_CHAIN (w))
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_i_mark_weak_vector (SCM w)
|
||||||
|
{
|
||||||
|
SCM_I_SET_WVECT_GC_CHAIN (w, weak_vectors);
|
||||||
|
weak_vectors = w;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
scm_i_mark_weak_vector_non_weaks (SCM w)
|
||||||
|
{
|
||||||
|
int again = 0;
|
||||||
|
|
||||||
|
if (SCM_IS_WHVEC_ANY (w))
|
||||||
{
|
{
|
||||||
if (!SCM_IS_WHVEC_ANY (w))
|
SCM *ptr;
|
||||||
|
long n = SCM_I_WVECT_LENGTH (w);
|
||||||
|
long j;
|
||||||
|
int weak_keys = SCM_IS_WHVEC (w) || SCM_IS_WHVEC_B (w);
|
||||||
|
int weak_values = SCM_IS_WHVEC_V (w) || SCM_IS_WHVEC_B (w);
|
||||||
|
|
||||||
|
ptr = SCM_I_WVECT_GC_WVELTS (w);
|
||||||
|
|
||||||
|
for (j = 0; j < n; ++j)
|
||||||
{
|
{
|
||||||
register long j, n;
|
SCM alist, slow_alist;
|
||||||
|
int slow_toggle = 0;
|
||||||
|
|
||||||
ptr = SCM_I_WVECT_GC_WVELTS (w);
|
/* We do not set the mark bits of the alist spine cells here
|
||||||
n = SCM_I_WVECT_LENGTH (w);
|
since we do not want to ever create the situation where a
|
||||||
for (j = 0; j < n; ++j)
|
marked cell references an unmarked cell (except in
|
||||||
if (UNMARKED_CELL_P (ptr[j]))
|
scm_gc_mark, where the referenced cells will be marked
|
||||||
ptr[j] = SCM_BOOL_F;
|
immediately). Thus, we can not use mark bits to stop us
|
||||||
}
|
from looping indefinitely over a cyclic alist. Instead,
|
||||||
/* check if we should scan the alist vector here (hashtables
|
we use the standard tortoise and hare trick to catch
|
||||||
have their own scan function in hashtab.c). */
|
cycles. The fast walker does the work, and stops when it
|
||||||
else if (!SCM_WVECT_NOSCAN_P (w))
|
catches the slow walker to ensure that the whole cycle
|
||||||
{
|
has been worked on.
|
||||||
SCM obj = w;
|
*/
|
||||||
register long n = SCM_I_WVECT_LENGTH (w);
|
|
||||||
register long j;
|
|
||||||
int weak_keys = SCM_IS_WHVEC (obj) || SCM_IS_WHVEC_B (obj);
|
|
||||||
int weak_values = SCM_IS_WHVEC_V (obj) || SCM_IS_WHVEC_B (obj);
|
|
||||||
|
|
||||||
ptr = SCM_I_WVECT_GC_WVELTS (w);
|
alist = slow_alist = ptr[j];
|
||||||
|
|
||||||
for (j = 0; j < n; ++j)
|
while (scm_is_pair (alist))
|
||||||
{
|
{
|
||||||
SCM * fixup;
|
SCM elt = SCM_CAR (alist);
|
||||||
SCM alist;
|
|
||||||
|
|
||||||
fixup = ptr + j;
|
if (UNMARKED_CELL_P (elt))
|
||||||
alist = *fixup;
|
|
||||||
|
|
||||||
while (scm_is_pair (alist)
|
|
||||||
&& scm_is_pair (SCM_CAR (alist)))
|
|
||||||
{
|
{
|
||||||
SCM key;
|
if (scm_is_pair (elt))
|
||||||
SCM value;
|
|
||||||
|
|
||||||
key = SCM_CAAR (alist);
|
|
||||||
value = SCM_CDAR (alist);
|
|
||||||
if ( (weak_keys && UNMARKED_CELL_P (key))
|
|
||||||
|| (weak_values && UNMARKED_CELL_P (value)))
|
|
||||||
{
|
{
|
||||||
*fixup = SCM_CDR (alist);
|
SCM key = SCM_CAR (elt);
|
||||||
|
SCM value = SCM_CDR (elt);
|
||||||
|
|
||||||
|
if (!((weak_keys && UNMARKED_CELL_P (key))
|
||||||
|
|| (weak_values && UNMARKED_CELL_P (value))))
|
||||||
|
{
|
||||||
|
/* The item should be kept. We need to mark it
|
||||||
|
recursively.
|
||||||
|
*/
|
||||||
|
scm_gc_mark (elt);
|
||||||
|
again = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
fixup = SCM_CDRLOC (alist);
|
{
|
||||||
alist = SCM_CDR (alist);
|
/* A non-pair cell element. This should not
|
||||||
|
appear in a real alist, but when it does, we
|
||||||
|
need to keep it.
|
||||||
|
*/
|
||||||
|
scm_gc_mark (elt);
|
||||||
|
again = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
alist = SCM_CDR (alist);
|
||||||
|
|
||||||
|
if (slow_toggle && scm_is_pair (slow_alist))
|
||||||
|
{
|
||||||
|
slow_alist = SCM_CDR (slow_alist);
|
||||||
|
slow_toggle = !slow_toggle;
|
||||||
|
if (scm_is_eq (slow_alist, alist))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!scm_is_pair (alist))
|
||||||
|
scm_gc_mark (alist);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return again;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
scm_i_mark_weak_vectors_non_weaks ()
|
||||||
|
{
|
||||||
|
int again = 0;
|
||||||
|
SCM w = weak_vectors;
|
||||||
|
while (!scm_is_null (w))
|
||||||
|
{
|
||||||
|
if (scm_i_mark_weak_vector_non_weaks (w))
|
||||||
|
again = 1;
|
||||||
|
w = SCM_I_WVECT_GC_CHAIN (w);
|
||||||
|
}
|
||||||
|
return again;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
scm_i_remove_weaks (SCM w)
|
||||||
|
{
|
||||||
|
SCM *ptr = SCM_I_WVECT_GC_WVELTS (w);
|
||||||
|
size_t n = SCM_I_WVECT_LENGTH (w);
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!SCM_IS_WHVEC_ANY (w))
|
||||||
|
{
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
if (UNMARKED_CELL_P (ptr[i]))
|
||||||
|
ptr[i] = SCM_BOOL_F;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
size_t delta = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < n; ++i)
|
||||||
|
{
|
||||||
|
SCM alist, *fixup;
|
||||||
|
|
||||||
|
fixup = ptr + i;
|
||||||
|
alist = *fixup;
|
||||||
|
while (scm_is_pair (alist) && !SCM_GC_MARK_P (alist))
|
||||||
|
{
|
||||||
|
if (UNMARKED_CELL_P (SCM_CAR (alist)))
|
||||||
|
{
|
||||||
|
*fixup = SCM_CDR (alist);
|
||||||
|
delta++;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SCM_SET_GC_MARK (alist);
|
||||||
|
fixup = SCM_CDRLOC (alist);
|
||||||
|
}
|
||||||
|
alist = *fixup;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
if (delta)
|
||||||
|
fprintf (stderr, "vector %p, delta %d\n", w, delta);
|
||||||
|
#endif
|
||||||
|
SCM_I_SET_WVECT_DELTA (w, delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scm_i_remove_weaks_from_weak_vectors ()
|
||||||
|
{
|
||||||
|
SCM w = weak_vectors;
|
||||||
|
while (!scm_is_null (w))
|
||||||
|
{
|
||||||
|
scm_i_remove_weaks (w);
|
||||||
|
w = SCM_I_WVECT_GC_CHAIN (w);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void
|
|
||||||
scm_weaks_prehistory ()
|
|
||||||
{
|
|
||||||
scm_c_hook_add (&scm_before_mark_c_hook, scm_weak_vector_gc_init, 0, 0);
|
|
||||||
scm_c_hook_add (&scm_before_sweep_c_hook, scm_mark_weak_vector_spines, 0, 0);
|
|
||||||
scm_c_hook_add (&scm_after_sweep_c_hook, scm_scan_weak_vectors, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
SCM
|
SCM
|
||||||
scm_init_weaks_builtins ()
|
scm_init_weaks_builtins ()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -28,17 +28,28 @@
|
||||||
|
|
||||||
#define SCM_WVECTF_WEAK_KEY 1
|
#define SCM_WVECTF_WEAK_KEY 1
|
||||||
#define SCM_WVECTF_WEAK_VALUE 2
|
#define SCM_WVECTF_WEAK_VALUE 2
|
||||||
#define SCM_WVECTF_NOSCAN 4
|
|
||||||
|
|
||||||
#define SCM_WVECT_WEAK_KEY_P(x) (SCM_I_WVECT_TYPE(x) & SCM_WVECTF_WEAK_KEY)
|
#define SCM_WVECT_WEAK_KEY_P(x) (SCM_I_WVECT_EXTRA(x) & SCM_WVECTF_WEAK_KEY)
|
||||||
#define SCM_WVECT_WEAK_VALUE_P(x) (SCM_I_WVECT_TYPE(x) & SCM_WVECTF_WEAK_VALUE)
|
#define SCM_WVECT_WEAK_VALUE_P(x) (SCM_I_WVECT_EXTRA(x) & SCM_WVECTF_WEAK_VALUE)
|
||||||
#define SCM_WVECT_NOSCAN_P(x) (SCM_I_WVECT_TYPE (x) & SCM_WVECTF_NOSCAN)
|
|
||||||
#define SCM_IS_WHVEC(X) (SCM_I_WVECT_TYPE (X) == 1)
|
|
||||||
#define SCM_IS_WHVEC_V(X) (SCM_I_WVECT_TYPE (X) == 2)
|
|
||||||
#define SCM_IS_WHVEC_B(X) (SCM_I_WVECT_TYPE (X) == 3)
|
|
||||||
#define SCM_IS_WHVEC_ANY(X) (SCM_I_WVECT_TYPE (X) != 0)
|
|
||||||
|
|
||||||
SCM_API SCM scm_weak_vectors;
|
/* The DELTA field is used by the abstract hash tables. During GC,
|
||||||
|
this field will be set to the number of items that have been
|
||||||
|
dropped. The abstract hash table will then use it to update its
|
||||||
|
item count. DELTA is unsigned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SCM_I_WVECT_DELTA(x) (SCM_I_WVECT_EXTRA(x) >> 3)
|
||||||
|
#define SCM_I_SET_WVECT_DELTA(x,n) (SCM_I_SET_WVECT_EXTRA \
|
||||||
|
((x), ((SCM_I_WVECT_EXTRA (x) & 7) \
|
||||||
|
| ((n) << 3))))
|
||||||
|
|
||||||
|
#define SCM_I_WVECT_TYPE(x) (SCM_I_WVECT_EXTRA(x) & 7)
|
||||||
|
#define SCM_I_SET_WVECT_TYPE(x,t) (SCM_I_SET_WVECT_EXTRA \
|
||||||
|
((x), (SCM_I_WVECT_EXTRA (x) & ~7) | (t)))
|
||||||
|
#define SCM_IS_WHVEC(X) (SCM_I_WVECT_TYPE (X) == 1)
|
||||||
|
#define SCM_IS_WHVEC_V(X) (SCM_I_WVECT_TYPE (X) == 2)
|
||||||
|
#define SCM_IS_WHVEC_B(X) (SCM_I_WVECT_TYPE (X) == 3)
|
||||||
|
#define SCM_IS_WHVEC_ANY(X) (SCM_I_WVECT_TYPE (X) != 0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -51,10 +62,14 @@ SCM_API SCM scm_make_doubly_weak_alist_vector (SCM k);
|
||||||
SCM_API SCM scm_weak_key_alist_vector_p (SCM x);
|
SCM_API SCM scm_weak_key_alist_vector_p (SCM x);
|
||||||
SCM_API SCM scm_weak_value_alist_vector_p (SCM x);
|
SCM_API SCM scm_weak_value_alist_vector_p (SCM x);
|
||||||
SCM_API SCM scm_doubly_weak_alist_vector_p (SCM x);
|
SCM_API SCM scm_doubly_weak_alist_vector_p (SCM x);
|
||||||
SCM_API void scm_weaks_prehistory (void);
|
|
||||||
SCM_API SCM scm_init_weaks_builtins (void);
|
SCM_API SCM scm_init_weaks_builtins (void);
|
||||||
SCM_API void scm_init_weaks (void);
|
SCM_API void scm_init_weaks (void);
|
||||||
|
|
||||||
|
SCM_API void scm_i_init_weak_vectors_for_gc (void);
|
||||||
|
SCM_API void scm_i_mark_weak_vector (SCM w);
|
||||||
|
SCM_API int scm_i_mark_weak_vectors_non_weaks (void);
|
||||||
|
SCM_API void scm_i_remove_weaks_from_weak_vectors (void);
|
||||||
|
|
||||||
#endif /* SCM_WEAKS_H */
|
#endif /* SCM_WEAKS_H */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue