summaryrefslogtreecommitdiff
path: root/runtime/weak.c
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/weak.c')
-rw-r--r--runtime/weak.c120
1 files changed, 76 insertions, 44 deletions
diff --git a/runtime/weak.c b/runtime/weak.c
index 1c5444b524..6ac6e22568 100644
--- a/runtime/weak.c
+++ b/runtime/weak.c
@@ -275,61 +275,93 @@ CAMLprim value caml_weak_get (value ar, value n)
return caml_ephe_get_key(ar, n);
}
-static value ephe_get_field_copy (value e, mlsize_t offset)
+/* Copy the contents of an object from `from` to `to` (which is
+ * already allocated and has the necessary header word). Darken
+ * any pointer fields. */
+
+static void ephe_copy_and_darken(value from, value to)
{
- CAMLparam1 (e);
- CAMLlocal2 (res, elt);
- mlsize_t i, infix_offs = 0;
- value v; /* Caution: this is NOT a local root. */
- value f;
+ mlsize_t i = 0; /* size of non-scannable prefix */
- clean_field(e, offset);
- v = Field(e, offset);
- if (v == caml_ephe_none) {
- res = Val_none;
- goto out;
+ CAMLassert(Is_block(from));
+ CAMLassert(Is_block(to));
+ CAMLassert(Tag_val(from) == Tag_val(to));
+ CAMLassert(Tag_val(from) != Infix_tag);
+ CAMLassert(Wosize_val(from) == Wosize_val(to));
+
+ if (Tag_val(from) > No_scan_tag) {
+ i = Wosize_val(to);
+ }
+ else if (Tag_val(from) == Closure_tag) {
+ i = Start_env_closinfo(Closinfo_val(from));
}
- /** Don't copy custom_block #7279 */
- if (Is_block(v) && Tag_val(v) != Custom_tag) {
- if (Tag_val(v) == Infix_tag) {
- infix_offs = Infix_offset_val(v);
- v -= infix_offs;
- }
- elt = caml_alloc (Wosize_val(v), Tag_val(v));
+ /* Copy non-scannable prefix */
+ memcpy (Bp_val(to), Bp_val(from), Bsize_wsize(i));
+
+ /* Copy and darken scannable fields */
+ caml_domain_state* domain_state = Caml_state;
+ while (i < Wosize_val(to)) {
+ value field = Field(from, i);
+ caml_darken (domain_state, field, 0);
+ Store_field(to, i, field);
+ ++ i;
+ }
+}
+static value ephe_get_field_copy (value e, mlsize_t offset)
+{
+ CAMLparam1 (e);
+ CAMLlocal3 (res, val, copy);
+ mlsize_t infix_offs = 0;
+
+ copy = Val_unit;
+ /* Loop in case allocating the copy triggers a GC which modifies the
+ * ephemeron or the value. In the common case, we go around this
+ * loop 1.5 times. */
+ while (1) {
clean_field(e, offset);
- v = Field(e, offset);
- if (v == caml_ephe_none) CAMLreturn (Val_none);
+ val = Field(e, offset);
- if (Tag_val(v) == Infix_tag) {
- infix_offs = Infix_offset_val(v);
- v -= infix_offs;
+ if (val == caml_ephe_none) {
+ res = Val_none;
+ goto out;
}
+ infix_offs = 0;
- if (Tag_val(v) < No_scan_tag) {
- caml_domain_state* domain_state = Caml_state;
- i = 0;
- if (Tag_val (v) == Closure_tag) {
- /* Direct copy of the code pointers and closure info fields */
- i = Start_env_closinfo(Closinfo_val(v));
- memcpy (Bp_val (elt), Bp_val (v), Bsize_wsize (i));
- }
- /* Field-by-field copy and darkening of the remaining fields */
- for (/*nothing*/; i < Wosize_val(v); i++) {
- f = Field(v, i);
- caml_darken (domain_state, f, 0);
- Store_field(elt, i, f);
- }
- } else {
- memmove (Bp_val(elt), Bp_val(v), Bosize_val(v));
+ /* Don't copy immediates or custom blocks #7279 */
+ if (!Is_block(val) || Tag_val(val) == Custom_tag) {
+ copy = val;
+ goto some;
}
- } else {
- Field(e, offset) = elt = v;
+
+ if (Tag_val(val) == Infix_tag) {
+ infix_offs = Infix_offset_val(val);
+ val -= infix_offs;
+ }
+
+ if (copy != Val_unit &&
+ (Tag_val(val) == Tag_val(copy)) &&
+ (Wosize_val(val) == Wosize_val(copy))) {
+ /* The copy we allocated (on a previous iteration) is large
+ * enough and has the right header bits for us to copy the
+ * contents of val into it. Note that we don't care whether val
+ * has changed since we allocated copy. */
+ break;
+ }
+
+ /* This allocation could provoke a GC, which could change the
+ * header or size of val (e.g. in a finalizer). So we go around
+ * the loop to read val again. */
+ copy = caml_alloc (Wosize_val(val), Tag_val(val));
+ val = Val_unit;
}
- res = caml_alloc_small (1, Tag_some);
- Field(res, 0) = elt + infix_offs;
- out:
+
+ ephe_copy_and_darken(val, copy);
+
+some:
+ res = caml_alloc_some(copy + infix_offs);
+out:
/* run GC and memprof callbacks */
caml_process_pending_actions();
CAMLreturn(res);