244 lines
6.9 KiB
C
244 lines
6.9 KiB
C
/***********************************************************************/
|
|
/* */
|
|
/* OCaml */
|
|
/* */
|
|
/* Damien Doligez, projet Moscova, INRIA Rocquencourt */
|
|
/* */
|
|
/* Copyright 2000 Institut National de Recherche en Informatique et */
|
|
/* en Automatique. All rights reserved. This file is distributed */
|
|
/* under the terms of the GNU Library General Public License, with */
|
|
/* the special exception on linking described in file ../LICENSE. */
|
|
/* */
|
|
/***********************************************************************/
|
|
|
|
/* Handling of finalised values. */
|
|
|
|
#include "caml/callback.h"
|
|
#include "caml/fail.h"
|
|
#include "caml/mlvalues.h"
|
|
#include "caml/roots.h"
|
|
#include "caml/signals.h"
|
|
|
|
struct final {
|
|
value fun;
|
|
value val;
|
|
int offset;
|
|
};
|
|
|
|
static struct final *final_table = NULL;
|
|
static uintnat old = 0, young = 0, size = 0;
|
|
/* [0..old) : finalisable set
|
|
[old..young) : recent set
|
|
[young..size) : free space
|
|
*/
|
|
|
|
struct to_do {
|
|
struct to_do *next;
|
|
int size;
|
|
struct final item[1]; /* variable size */
|
|
};
|
|
|
|
static struct to_do *to_do_hd = NULL;
|
|
static struct to_do *to_do_tl = NULL;
|
|
|
|
/* [size] is a number of elements for the [to_do.item] array */
|
|
static void alloc_to_do (int size)
|
|
{
|
|
struct to_do *result = malloc (sizeof (struct to_do)
|
|
+ size * sizeof (struct final));
|
|
if (result == NULL) caml_fatal_error ("out of memory");
|
|
result->next = NULL;
|
|
result->size = size;
|
|
if (to_do_tl == NULL){
|
|
to_do_hd = result;
|
|
to_do_tl = result;
|
|
}else{
|
|
Assert (to_do_tl->next == NULL);
|
|
to_do_tl->next = result;
|
|
to_do_tl = result;
|
|
}
|
|
}
|
|
|
|
/* Find white finalisable values, move them to the finalising set, and
|
|
darken them.
|
|
*/
|
|
void caml_final_update (void)
|
|
{
|
|
uintnat i, j, k;
|
|
uintnat todo_count = 0;
|
|
|
|
Assert (old <= young);
|
|
for (i = 0; i < old; i++){
|
|
Assert (Is_block (final_table[i].val));
|
|
Assert (Is_in_heap (final_table[i].val));
|
|
if (Is_white_val (final_table[i].val)) ++ todo_count;
|
|
}
|
|
|
|
if (todo_count > 0){
|
|
alloc_to_do (todo_count);
|
|
j = k = 0;
|
|
for (i = 0; i < old; i++){
|
|
Assert (Is_block (final_table[i].val));
|
|
Assert (Is_in_heap (final_table[i].val));
|
|
Assert (Tag_val (final_table[i].val) != Forward_tag);
|
|
if (Is_white_val (final_table[i].val)){
|
|
to_do_tl->item[k++] = final_table[i];
|
|
}else{
|
|
final_table[j++] = final_table[i];
|
|
}
|
|
}
|
|
CAMLassert (i == old);
|
|
old = j;
|
|
for(;i < young; i++){
|
|
final_table[j++] = final_table[i];
|
|
}
|
|
young = j;
|
|
to_do_tl->size = k;
|
|
for (i = 0; i < k; i++){
|
|
/* Note that item may already be dark due to multiple entries in
|
|
the final table. */
|
|
caml_darken (to_do_tl->item[i].val, NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
static int running_finalisation_function = 0;
|
|
|
|
/* Call the finalisation functions for the finalising set.
|
|
Note that this function must be reentrant.
|
|
*/
|
|
void caml_final_do_calls (void)
|
|
{
|
|
struct final f;
|
|
value res;
|
|
|
|
if (running_finalisation_function) return;
|
|
if (caml_finalise_begin_hook != NULL) (*caml_finalise_begin_hook) ();
|
|
if (to_do_hd != NULL){
|
|
if (caml_finalise_begin_hook != NULL) (*caml_finalise_begin_hook) ();
|
|
caml_gc_message (0x80, "Calling finalisation functions.\n", 0);
|
|
while (1){
|
|
while (to_do_hd != NULL && to_do_hd->size == 0){
|
|
struct to_do *next_hd = to_do_hd->next;
|
|
free (to_do_hd);
|
|
to_do_hd = next_hd;
|
|
if (to_do_hd == NULL) to_do_tl = NULL;
|
|
}
|
|
if (to_do_hd == NULL) break;
|
|
Assert (to_do_hd->size > 0);
|
|
-- to_do_hd->size;
|
|
f = to_do_hd->item[to_do_hd->size];
|
|
running_finalisation_function = 1;
|
|
res = caml_callback_exn (f.fun, f.val + f.offset);
|
|
running_finalisation_function = 0;
|
|
if (Is_exception_result (res)) caml_raise (Extract_exception (res));
|
|
}
|
|
caml_gc_message (0x80, "Done calling finalisation functions.\n", 0);
|
|
if (caml_finalise_end_hook != NULL) (*caml_finalise_end_hook) ();
|
|
}
|
|
if (caml_finalise_end_hook != NULL) (*caml_finalise_end_hook) ();
|
|
}
|
|
|
|
/* Call a scanning_action [f] on [x]. */
|
|
#define Call_action(f,x) (*(f)) ((x), &(x))
|
|
|
|
/* Call [*f] on the closures of the finalisable set and
|
|
the closures and values of the finalising set.
|
|
This is called by the major GC through [caml_darken_all_roots].
|
|
*/
|
|
void caml_final_do_strong_roots (scanning_action f)
|
|
{
|
|
uintnat i;
|
|
struct to_do *todo;
|
|
|
|
Assert (old <= young);
|
|
for (i = 0; i < young; i++) Call_action (f, final_table[i].fun);
|
|
|
|
for (todo = to_do_hd; todo != NULL; todo = todo->next){
|
|
for (i = 0; i < todo->size; i++){
|
|
Call_action (f, todo->item[i].fun);
|
|
Call_action (f, todo->item[i].val);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Call [*f] on the values of the finalisable set.
|
|
This is called directly by the compactor.
|
|
*/
|
|
void caml_final_do_weak_roots (scanning_action f)
|
|
{
|
|
uintnat i;
|
|
|
|
CAMLassert (old <= young);
|
|
for (i = 0; i < young; i++) Call_action (f, final_table[i].val);
|
|
}
|
|
|
|
/* Call [*f] on the closures and values of the recent set.
|
|
This is called by the minor GC through [caml_oldify_local_roots].
|
|
*/
|
|
void caml_final_do_young_roots (scanning_action f)
|
|
{
|
|
uintnat i;
|
|
|
|
Assert (old <= young);
|
|
for (i = old; i < young; i++){
|
|
Call_action (f, final_table[i].fun);
|
|
Call_action (f, final_table[i].val);
|
|
}
|
|
}
|
|
|
|
/* Empty the recent set into the finalisable set.
|
|
This is called at the end of each minor collection.
|
|
The minor heap must be empty when this is called.
|
|
*/
|
|
void caml_final_empty_young (void)
|
|
{
|
|
old = young;
|
|
}
|
|
|
|
/* Put (f,v) in the recent set. */
|
|
CAMLprim value caml_final_register (value f, value v)
|
|
{
|
|
if (!Is_block (v)
|
|
|| !Is_in_heap_or_young(v)
|
|
|| Tag_val (v) == Lazy_tag
|
|
|| Tag_val (v) == Double_tag
|
|
|| Tag_val (v) == Forward_tag) {
|
|
caml_invalid_argument ("Gc.finalise");
|
|
}
|
|
Assert (old <= young);
|
|
|
|
if (young >= size){
|
|
if (final_table == NULL){
|
|
uintnat new_size = 30;
|
|
final_table = caml_stat_alloc (new_size * sizeof (struct final));
|
|
Assert (old == 0);
|
|
Assert (young == 0);
|
|
size = new_size;
|
|
}else{
|
|
uintnat new_size = size * 2;
|
|
final_table = caml_stat_resize (final_table,
|
|
new_size * sizeof (struct final));
|
|
size = new_size;
|
|
}
|
|
}
|
|
Assert (young < size);
|
|
final_table[young].fun = f;
|
|
if (Tag_val (v) == Infix_tag){
|
|
final_table[young].offset = Infix_offset_val (v);
|
|
final_table[young].val = v - Infix_offset_val (v);
|
|
}else{
|
|
final_table[young].offset = 0;
|
|
final_table[young].val = v;
|
|
}
|
|
++ young;
|
|
|
|
return Val_unit;
|
|
}
|
|
|
|
CAMLprim value caml_final_release (value unit)
|
|
{
|
|
running_finalisation_function = 0;
|
|
return Val_unit;
|
|
}
|