ocaml/runtime/globroots.c

188 lines
6.0 KiB
C

/**************************************************************************/
/* */
/* OCaml */
/* */
/* Xavier Leroy, projet Cristal, INRIA Rocquencourt */
/* */
/* Copyright 2001 Institut National de Recherche en Informatique et */
/* en Automatique. */
/* */
/* All rights reserved. This file is distributed under the terms of */
/* the GNU Lesser General Public License version 2.1, with the */
/* special exception on linking described in the file LICENSE. */
/* */
/**************************************************************************/
#define CAML_INTERNALS
/* Registration of global memory roots */
#include "caml/mlvalues.h"
#include "caml/roots.h"
#include "caml/globroots.h"
#include "caml/skiplist.h"
/* The three global root lists.
Each is represented by a skip list with the key being the address
of the root. (The associated data field is unused.) */
struct skiplist caml_global_roots = SKIPLIST_STATIC_INITIALIZER;
/* mutable roots, don't know whether old or young */
struct skiplist caml_global_roots_young = SKIPLIST_STATIC_INITIALIZER;
/* generational roots pointing to minor or major heap */
struct skiplist caml_global_roots_old = SKIPLIST_STATIC_INITIALIZER;
/* generational roots pointing to major heap */
/* The invariant of the generational roots is the following:
- If the global root contains a pointer to the minor heap, then the root is
in [caml_global_roots_young];
- If the global root contains a pointer to the major heap, then the root is
in [caml_global_roots_old] or in [caml_global_roots_young];
- Otherwise (the root contains a pointer outside of the heap or an integer),
then neither [caml_global_roots_young] nor [caml_global_roots_old] contain
it.
*/
/* Insertion and deletion */
Caml_inline void caml_insert_global_root(struct skiplist * list, value * r)
{
caml_skiplist_insert(list, (uintnat) r, 0);
}
Caml_inline void caml_delete_global_root(struct skiplist * list, value * r)
{
caml_skiplist_remove(list, (uintnat) r);
}
/* Iterate a GC scanning action over a global root list */
static void caml_iterate_global_roots(scanning_action f,
struct skiplist * rootlist)
{
FOREACH_SKIPLIST_ELEMENT(e, rootlist, {
value * r = (value *) (e->key);
f(*r, r);
})
}
/* Register a global C root of the mutable kind */
CAMLexport void caml_register_global_root(value *r)
{
CAMLassert (((intnat) r & 3) == 0); /* compact.c demands this (for now) */
caml_insert_global_root(&caml_global_roots, r);
}
/* Un-register a global C root of the mutable kind */
CAMLexport void caml_remove_global_root(value *r)
{
caml_delete_global_root(&caml_global_roots, r);
}
enum gc_root_class {
YOUNG,
OLD,
UNTRACKED
};
static enum gc_root_class classify_gc_root(value v)
{
if(!Is_block(v)) return UNTRACKED;
if(Is_young(v)) return YOUNG;
#ifndef NO_NAKED_POINTERS
if(!Is_in_heap(v)) return UNTRACKED;
#endif
return OLD;
}
/* Register a global C root of the generational kind */
CAMLexport void caml_register_generational_global_root(value *r)
{
CAMLassert (((intnat) r & 3) == 0); /* compact.c demands this (for now) */
switch(classify_gc_root(*r)) {
case YOUNG:
caml_insert_global_root(&caml_global_roots_young, r);
break;
case OLD:
caml_insert_global_root(&caml_global_roots_old, r);
break;
case UNTRACKED: break;
}
}
/* Un-register a global C root of the generational kind */
CAMLexport void caml_remove_generational_global_root(value *r)
{
switch(classify_gc_root(*r)) {
case OLD:
caml_delete_global_root(&caml_global_roots_old, r);
/* Fallthrough: the root can be in the young list while actually
being in the major heap. */
case YOUNG:
caml_delete_global_root(&caml_global_roots_young, r);
break;
case UNTRACKED: break;
}
}
/* Modify the value of a global C root of the generational kind */
CAMLexport void caml_modify_generational_global_root(value *r, value newval)
{
enum gc_root_class c;
/* See PRs #4704, #607 and #8656 */
switch(classify_gc_root(newval)) {
case YOUNG:
c = classify_gc_root(*r);
if(c == OLD)
caml_delete_global_root(&caml_global_roots_old, r);
if(c != YOUNG)
caml_insert_global_root(&caml_global_roots_young, r);
break;
case OLD:
/* If the old class is YOUNG, then we do not need to do
anything: It is OK to have a root in roots_young that
suddenly points to the old generation -- the next minor GC
will take care of that. */
if(classify_gc_root(*r) == UNTRACKED)
caml_insert_global_root(&caml_global_roots_old, r);
break;
case UNTRACKED:
caml_remove_generational_global_root(r);
break;
}
*r = newval;
}
/* Scan all global roots */
void caml_scan_global_roots(scanning_action f)
{
caml_iterate_global_roots(f, &caml_global_roots);
caml_iterate_global_roots(f, &caml_global_roots_young);
caml_iterate_global_roots(f, &caml_global_roots_old);
}
/* Scan global roots for a minor collection */
void caml_scan_global_young_roots(scanning_action f)
{
caml_iterate_global_roots(f, &caml_global_roots);
caml_iterate_global_roots(f, &caml_global_roots_young);
/* Move young roots to old roots */
FOREACH_SKIPLIST_ELEMENT(e, &caml_global_roots_young, {
value * r = (value *) (e->key);
caml_insert_global_root(&caml_global_roots_old, r);
});
caml_skiplist_empty(&caml_global_roots_young);
}