Skip to content

Ephemeron segfault with missing write barrier #9391

@stedolan

Description

@stedolan

Ephemerons don't use the normal write barrier, and because they're marked incrementally this can lead to the lost object problem, where an object becomes reachable only from an ephemeron that's already marked and never ends up getting marked.

It's difficult to trigger because it relies on the timing of incremental GC, but here's an example that hits the bug on trunk.

module E = Ephemeron.K1

let key = ref 1
let mk_ephe () =
  let e = E.create () in
  E.set_key e key;
  e

let eph = mk_ephe ()
let other = ref "beep"

let () =
  for stop = 100 to 2000 do
    E.set_data eph (ref "hello");
    Gc.major ();
    Gc.minor (); Gc.major_slice 1 |> ignore; Gc.major_slice 1 |> ignore;
    (* let _ = Array.init 1 (fun _ -> mk_ephe ()) in *)
    let e = mk_ephe () in
    (* let _ = Array.init 3 (fun _ -> mk_ephe ()) in *)
    for i = 1 to 2000 do
      Gc.major_slice 1 |> ignore;
      if i = stop then begin
          E.blit_data eph e;
          E.set_data eph other;
        end;
    done;
    let r = match E.get_data e with None -> assert false | Some r -> r in
    Printf.printf "[%03d] %s\n%!" stop !r;
    Gc.full_major ();
  done

I'm not sure what the fix is. It may be as simple as adding darkening in blit_data (and maybe set_data?), but I don't understand what the marking invariants are for ephemerons.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions