/**************************************************************************/
/*                                                                        */
/*                                 OCaml                                  */
/*                                                                        */
/*              Damien Doligez, projet Para, INRIA Rocquencourt           */
/*                                                                        */
/*   Copyright 1996 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.          */
/*                                                                        */
/**************************************************************************/

#ifndef CAML_MINOR_GC_H
#define CAML_MINOR_GC_H

#include "address_class.h"
#include "misc.h"
#include "config.h"

#define caml_young_end Caml_state->young_end
#define caml_young_ptr Caml_state->young_ptr
#define caml_young_start Caml_state->young_start
#define caml_young_limit Caml_state->young_limit
#define caml_young_alloc_start Caml_state->young_start
#define caml_young_alloc_end Caml_state->young_end
#define caml_minor_heap_wsz Caml_state->minor_heap_wsz


#define CAML_TABLE_STRUCT(t) { \
  t *base;                     \
  t *end;                      \
  t *threshold;                \
  t *ptr;                      \
  t *limit;                    \
  asize_t size;                \
  asize_t reserve;             \
}

#ifdef __cplusplus
extern "C" {
#endif

/* Count of the total number of minor collections performed by the program */
CAMLextern atomic_uintnat caml_minor_collections_count;

/* The epoch number for major slice. Used to trigger major slices.
   Always, [caml_major_slice_epoch <= caml_minor_collections_count] */
CAMLextern atomic_uintnat caml_major_slice_epoch;

struct caml_ref_table CAML_TABLE_STRUCT(value *);

struct caml_ephe_ref_elt {
  value ephe;      /* an ephemeron in major heap */
  mlsize_t offset; /* the offset that points in the minor heap  */
  value locked;    /* only used during minor GC; see minor_gc.c */
};
struct caml_ephe_ref_table CAML_TABLE_STRUCT(struct caml_ephe_ref_elt);

struct caml_custom_elt {
  value block;     /* The finalized block in the minor heap. */
  mlsize_t mem;    /* The parameters for adjusting GC speed. */
  mlsize_t max;
};
struct caml_custom_table CAML_TABLE_STRUCT(struct caml_custom_elt);

struct caml_minor_tables {
  struct caml_ref_table major_ref;
  struct caml_ephe_ref_table ephe_ref;
  struct caml_custom_table custom;
};

CAMLextern void caml_minor_collection (void);

#ifdef __cplusplus
}
#endif

#ifdef CAML_INTERNALS
extern void caml_set_minor_heap_size (asize_t); /* size in bytes */
extern void caml_empty_minor_heap_no_major_slice_from_stw
  (caml_domain_state* domain, void* unused, int participating_count,
    caml_domain_state** participating); /* in STW */
extern int caml_try_empty_minor_heap_on_all_domains(void); /* out STW */
extern void caml_empty_minor_heaps_once(void); /* out STW */
void caml_alloc_small_dispatch (caml_domain_state* domain,
                                intnat wosize, int flags,
                                int nallocs, unsigned char* encoded_alloc_lens);
header_t caml_get_header_val(value v);
void caml_alloc_table (struct caml_ref_table *tbl, asize_t sz, asize_t rsv);
extern void caml_realloc_ref_table (struct caml_ref_table *);
extern void caml_realloc_ephe_ref_table (struct caml_ephe_ref_table *);
extern void caml_realloc_custom_table (struct caml_custom_table *);
struct caml_minor_tables* caml_alloc_minor_tables(void);
void caml_free_minor_tables(struct caml_minor_tables*);
void caml_empty_minor_heap_setup(caml_domain_state* domain, void *);

#ifdef DEBUG
extern int caml_debug_is_minor(value val);
extern int caml_debug_is_major(value val);
#endif

#define Ref_table_add(ref_table, x) do {                                \
    struct caml_ref_table* ref = (ref_table);                           \
    if (ref->ptr >= ref->limit) {                                       \
      CAMLassert (ref->ptr == ref->limit);                              \
      caml_realloc_ref_table (ref);                                     \
    }                                                                   \
    *ref->ptr++ = (value*)(x);                                          \
  } while (0)

Caml_inline void add_to_ephe_ref_table (struct caml_ephe_ref_table *tbl,
                                        value ar, mlsize_t offset)
{
  struct caml_ephe_ref_elt *ephe_ref;
  if (tbl->ptr >= tbl->limit){
    CAMLassert (tbl->ptr == tbl->limit);
    caml_realloc_ephe_ref_table (tbl);
  }
  ephe_ref = tbl->ptr++;
  ephe_ref->ephe = ar;
  ephe_ref->offset = offset;
  ephe_ref->locked = Val_unit;
  CAMLassert(ephe_ref->offset < Wosize_val(ephe_ref->ephe));
}

Caml_inline void add_to_custom_table (struct caml_custom_table *tbl, value v,
                                        mlsize_t mem, mlsize_t max)
{
  struct caml_custom_elt *elt;
  if (tbl->ptr >= tbl->limit){
    CAMLassert (tbl->ptr == tbl->limit);
    caml_realloc_custom_table (tbl);
  }
  elt = tbl->ptr++;
  elt->block = v;
  elt->mem = mem;
  elt->max = max;
}

#endif /* CAML_INTERNALS */

#endif /* CAML_MINOR_GC_H */
