File: custom.c

package info (click to toggle)
ocaml 5.3.0-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 43,124 kB
  • sloc: ml: 355,439; ansic: 51,636; sh: 25,098; asm: 5,413; makefile: 3,673; python: 919; javascript: 273; awk: 253; perl: 59; fortran: 21; cs: 9
file content (190 lines) | stat: -rw-r--r-- 7,294 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/**************************************************************************/
/*                                                                        */
/*                                 OCaml                                  */
/*                                                                        */
/*          Manuel Serrano and Xavier Leroy, 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 Lesser General Public License version 2.1, with the          */
/*   special exception on linking described in the file LICENSE.          */
/*                                                                        */
/**************************************************************************/

#define CAML_INTERNALS

#include <string.h>

#include "caml/alloc.h"
#include "caml/camlatomic.h"
#include "caml/custom.h"
#include "caml/fail.h"
#include "caml/gc_ctrl.h"
#include "caml/memory.h"
#include "caml/mlvalues.h"
#include "caml/shared_heap.h"
#include "caml/signals.h"
#include "caml/memprof.h"

uintnat caml_custom_major_ratio = Custom_major_ratio_def;
uintnat caml_custom_minor_ratio = Custom_minor_ratio_def;
uintnat caml_custom_minor_max_bsz = Custom_minor_max_bsz_def;

mlsize_t caml_custom_get_max_major (void)
{
  /* The major ratio is a percentage relative to the major heap size.
     A complete GC cycle will be done every time 2/3 of that much
     memory is allocated for blocks in the major heap.  Assuming
     constant allocation and deallocation rates, this means there are
     at most [M/100 * major-heap-size] bytes of floating garbage at
     any time.  The reason for a factor of 2/3 (or 1.5) is, roughly
     speaking, because the major GC takes 1.5 cycles (previous cycle +
     marking phase) before it starts to deallocate dead blocks
     allocated during the previous cycle.  [heap_size / 150] is really
     [heap_size * (2/3) / 100] (but faster). */
  return caml_heap_size(Caml_state->shared_heap) / 150
         * caml_custom_major_ratio;
}

/* [mem] is an amount of out-of-heap resources, in the same units as
   [max_major] and [max_minor]. When the cumulated amount of such
   resources reaches [max_minor] (for resources held by the minor
   heap) we do a minor collection; when it reaches [max_major] (for
   resources held by the major heap), we guarantee that a major cycle
   is done.

   If [max_major] is 0, then [mem] is a number of bytes and the actual
   limit is [caml_custom_get_max_major ()] computed at the
   time when the custom block is promoted to the major heap.
*/
static value alloc_custom_gen (const struct custom_operations * ops,
                               uintnat bsz,
                               mlsize_t mem,
                               mlsize_t max_major,
                               mlsize_t max_minor)
{
  mlsize_t wosize;
  CAMLparam0();
  CAMLlocal1(result);

  wosize = 1 + (bsz + sizeof(value) - 1) / sizeof(value);
  if (wosize <= Max_young_wosize && mem <= caml_custom_minor_max_bsz) {
    result = caml_alloc_small(wosize, Custom_tag);
    Custom_ops_val(result) = ops;
    if (ops->finalize != NULL || mem != 0) {
      /* Record the extra resources in case the block gets promoted. */
      add_to_custom_table (&Caml_state->minor_tables->custom, result,
                           mem, max_major);
      /* Keep track of extra resources held by custom block in
         minor heap. */
      if (mem != 0) {
        caml_adjust_minor_gc_speed (mem, max_minor);
      }
    }
  } else {
    result = caml_alloc_shr(wosize, Custom_tag);
    Custom_ops_val(result) = ops;
    caml_adjust_gc_speed(mem, max_major);
    result = caml_check_urgent_gc(result);
  }
  CAMLreturn(result);
}

Caml_inline mlsize_t get_max_minor (void)
{
  return
    Bsize_wsize (Caml_state->minor_heap_wsz) / 100 * caml_custom_minor_ratio;
}

CAMLexport value caml_alloc_custom(const struct custom_operations * ops,
                                   uintnat bsz,
                                   mlsize_t mem,
                                   mlsize_t max)
{
  mlsize_t max_major = max;
  mlsize_t max_minor = max == 0 ? get_max_minor() : max;
  return alloc_custom_gen (ops, bsz, mem, max_major, max_minor);
}

CAMLexport value caml_alloc_custom_mem(const struct custom_operations * ops,
                                       uintnat bsz,
                                       mlsize_t mem)
{
  value v = alloc_custom_gen (ops, bsz, mem, 0, get_max_minor());
  size_t mem_words = (mem + sizeof(value) - 1) / sizeof(value);
  caml_memprof_sample_block(v, mem_words, mem_words, CAML_MEMPROF_SRC_CUSTOM);
  return v;
}

struct custom_operations_list {
  const struct custom_operations * ops;
  struct custom_operations_list * next;
};

typedef _Atomic(struct custom_operations_list *) custom_operations_table;

/* Thread-safety: the tables are append-only lists, hence we only need
   a CAS loop update them. */
static void push_custom_ops(custom_operations_table * table,
                            const struct custom_operations * ops)
{
  struct custom_operations_list * l =
    caml_stat_alloc(sizeof(struct custom_operations_list));
  l->ops = ops;
  struct custom_operations_list * prev = atomic_load(table);
  do {
    l->next = prev;
  } while (!atomic_compare_exchange_weak(table, &prev, l));
}

static custom_operations_table custom_ops_table = NULL;

CAMLexport void
caml_register_custom_operations(const struct custom_operations * ops)
{
  CAMLassert(ops->identifier != NULL);
  CAMLassert(ops->deserialize != NULL);
  push_custom_ops(&custom_ops_table, ops);
}

struct custom_operations * caml_find_custom_operations(const char * ident)
{
  for (struct custom_operations_list *l = atomic_load(&custom_ops_table);
       l != NULL;
       l = l->next)
    if (strcmp(l->ops->identifier, ident) == 0)
      return (struct custom_operations*)l->ops;
  return NULL;
}

static custom_operations_table custom_ops_final_table = NULL;

struct custom_operations * caml_final_custom_operations(final_fun fn)
{
  struct custom_operations * ops;
  for (struct custom_operations_list *l = atomic_load(&custom_ops_final_table);
       l != NULL;
       l = l->next)
    if (l->ops->finalize == fn) return (struct custom_operations*)l->ops;
  ops = caml_stat_alloc(sizeof(struct custom_operations));
  ops->identifier = "_final";
  ops->finalize = fn;
  ops->compare = custom_compare_default;
  ops->hash = custom_hash_default;
  ops->serialize = custom_serialize_default;
  ops->deserialize = custom_deserialize_default;
  ops->compare_ext = custom_compare_ext_default;
  ops->fixed_length = custom_fixed_length_default;
  push_custom_ops(&custom_ops_final_table, ops);
  return ops;
}

void caml_init_custom_operations(void)
{
  caml_register_custom_operations(&caml_int32_ops);
  caml_register_custom_operations(&caml_nativeint_ops);
  caml_register_custom_operations(&caml_int64_ops);
  caml_register_custom_operations(&caml_ba_ops);
}