summaryrefslogtreecommitdiff
path: root/runtime/caml/weak.h
blob: 668d51001a0f55040cdcb5de4c3a7c22ef14acce (plain)
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
/**************************************************************************/
/*                                                                        */
/*                                 OCaml                                  */
/*                                                                        */
/*             Damien Doligez, projet Para, INRIA Rocquencourt            */
/*                                                                        */
/*   Copyright 1997 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.          */
/*                                                                        */
/**************************************************************************/

/* Operations on weak arrays */

#ifndef CAML_WEAK_H
#define CAML_WEAK_H

#include "mlvalues.h"
#include "memory.h"

#ifdef __cplusplus
extern "C" {
#endif

/** The requirements of the functions must be satisfied, it is
    unspecified what happens if they are not. The debugging runtime
    could check some of them. */

CAMLextern value caml_ephemeron_create(mlsize_t len);
/** Create an ephemeron with the given number of keys.
    This function allocates.
 */

CAMLextern mlsize_t caml_ephemeron_num_keys(value eph);
/** Return the number of key in the ephemeron. The valid key offset goes
    from [0] to the predecessor of the returned value. */

CAMLextern int caml_ephemeron_key_is_set(value eph, mlsize_t offset);
/** Return 1 if the key in the ephemeron at the given offset is set.
    Otherwise 0. The value [eph] must be an ephemeron and [offset] a
    valid key offset.
*/

CAMLextern void caml_ephemeron_set_key(value eph, mlsize_t offset, value k);
/** Set the key of the given ephemeron [eph] at the given offset
    [offset] to the given value [k]. The value [eph] must be an
    ephemeron, [offset] a valid key offset and [k] a block.
*/

CAMLextern void caml_ephemeron_unset_key(value eph, mlsize_t offset);
/** Unset the key of the given ephemeron at the given offset. The
    value [eph] must be an ephemeron and [offset] a valid key offset.
*/

CAMLextern int caml_ephemeron_get_key(value eph, mlsize_t offset, value *key);
/** Return 1 if the key in the ephemeron at the given offset is set.
    Otherwise 0. When returning 1, set [*key] to the pointed value.

    The value [eph] must be an ephemeron and [offset] a valid key
    offset.
*/

CAMLextern int caml_ephemeron_get_key_copy(value eph, mlsize_t offset,
                                           value *key);
/** Return 1 if the key in the ephemeron at the given offset is set.
    Otherwise 0. When returning 1, set [*key] to a shallow copy of the
    key. This function allocates.

    The value [eph] must be an ephemeron and [offset] a valid key
    offset.
*/

CAMLextern void caml_ephemeron_blit_key(value eph1, mlsize_t off1,
                                        value eph2, mlsize_t off2,
                                        mlsize_t len);
/** Fill the given range of keys of [eph2] with the given range of
    keys of [eph1]. Contrary to using caml_ephemeron_get_key followed
    by caml_ephemeron_set_key or caml_ephemeron_unset_key, this
    function does not prevent the incremental GC from erasing the
    value in its current cycle. The value [eph1] (resp. [eph2]) must
    be an ephemeron and the offsets between [off1] and [off1+len]
    (resp. between [off2] and [off2+offset]) must be valid keys of
    [eph1] (resp. [eph2]).
*/

CAMLextern int caml_ephemeron_data_is_set(value eph);
/** Return 1 if the data in the ephemeron is set.
    Otherwise 0. The value [eph] must be an ephemeron.
*/

CAMLextern void caml_ephemeron_set_data(value eph, value k);
/** Set the data of the given ephemeron [eph] to the given value
    [k]. The value [eph] must be an ephemeron and [k] a block.
*/

CAMLextern void caml_ephemeron_unset_data(value eph);
/** Unset the data of the given ephemeron. The value [eph] must be an
    ephemeron.
*/

CAMLextern int caml_ephemeron_get_data(value eph, value *data);
/** Return 1 if the data in the ephemeron at the given offset is set.
    Otherwise 0. When returning 1, set [*data] to the pointed value.

    The value [eph] must be an ephemeron and [offset] a valid key
    offset.
*/

CAMLextern int caml_ephemeron_get_data_copy(value eph, value *data);
/** Return 1 if the data in the ephemeron at the given offset is set.
    Otherwise 0. When returning 1, set [*data] to a shallow copy of
    the data. This function allocates.

    The value [eph] must be an ephemeron and [offset] a valid key
    offset.
*/

CAMLextern void caml_ephemeron_blit_data(value eph1, value eph2);
/** Sets the data of [eph2] to be the same as the data of [eph1].
    Contrary to using caml_ephemeron_get_data followed by
    caml_ephemeron_set_data or caml_ephemeron_unset_data, this
    function does not prevent the incremental GC from erasing the
    value in its current cycle. The values [eph1] and [eph2] must be
    ephemerons.
*/


#define caml_weak_array_length caml_ephemeron_num_keys
#define caml_weak_array_create caml_ephemeron_create
#define caml_weak_array_check caml_ephemeron_key_is_set
#define caml_weak_array_unset caml_ephemeron_unset_key
#define caml_weak_array_set caml_ephemeron_set_key
#define caml_weak_array_get caml_ephemeron_get_key
#define caml_weak_array_get_copy caml_ephemeron_get_key_copy
#define caml_weak_array_blit caml_ephemeron_blit_key

#ifdef CAML_INTERNALS

extern value caml_ephe_list_head;
extern value caml_ephe_none;


/** The first field 0:  weak list;
       second field 1:  data;
       others       2..:  keys;

    A weak pointer is an ephemeron with the data at caml_ephe_none
    If fields are added, don't forget to update weak.ml, [additional_values],
    and obj.ml, [Ephemeron.additional_values].


 */

#define CAML_EPHE_LINK_OFFSET 0
#define CAML_EPHE_DATA_OFFSET 1
#define CAML_EPHE_FIRST_KEY 2
#define CAML_EPHE_MAX_WOSIZE (Max_wosize - CAML_EPHE_FIRST_KEY)

/* In the header, in order to let major_gc.c
   and weak.c see the body of the function */
static inline void caml_ephe_clean (value v){
  value child;
  int release_data = 0;
  mlsize_t size, i;
  header_t hd;
  CAMLassert(caml_gc_phase == Phase_clean);

  hd = Hd_val (v);
  size = Wosize_hd (hd);
  for (i = 2; i < size; i++){
    child = Field (v, i);
  ephemeron_again:
    if (child != caml_ephe_none
        && Is_block (child) && Is_in_heap_or_young (child)){
      if (Tag_val (child) == Forward_tag){
        value f = Forward_val (child);
        if (Is_block (f)) {
          if (!Is_in_value_area(f) || Tag_val (f) == Forward_tag
              || Tag_val (f) == Lazy_tag || Tag_val (f) == Double_tag){
            /* Do not short-circuit the pointer. */
          }else{
            Field (v, i) = child = f;
            if (Is_block (f) && Is_young (f))
              add_to_ephe_ref_table(Caml_state->ephe_ref_table, v, i);
            goto ephemeron_again;
          }
        }
      }
      if (Is_white_val (child) && !Is_young (child)){
        release_data = 1;
        Field (v, i) = caml_ephe_none;
      }
    }
  }

  child = Field (v, 1);
  if(child != caml_ephe_none){
      if (release_data){
        Field (v, 1) = caml_ephe_none;
      } else {
        /* The mark phase must have marked it */
        CAMLassert( !(Is_block (child) && Is_in_heap (child)
                  && Is_white_val (child)) );
      }
  }
}

#endif /* CAML_INTERNALS */

#ifdef __cplusplus
}
#endif

#endif /* CAML_WEAK_H */