Set Bullet Behavior "Strict Subproofs".
Instance CompSpecs : compspecs. make_compspecs prog. Defined.
Definition Vprog : varspecs. mk_varspecs prog. Defined.
Definition tnode := Tstruct _node noattr.
(* Each thread's share of the head should act as permission to get the same share of any other node in the list. In fact, it might need to maintain a list of nodes it knows about (for which it owns a share) and retain the right to get a share of any new node it finds. In this model, each time it loads from next it checks whether it's finding a new node or one that it already owns, and gains a share or not accordingly. *)
(* Alternatively, it could just have the right to get *some* readable share of any other node. This will make it impossible to rejoin the shares or know that they're all recollected, but maybe that's fine. We could even try using a token factory. *)
(* A tail element with INT_MAX ensures that we never fall off the end. *)
Definition node_fun gsh1 gsh2 R a := let '(e0, g, p) := a in
EX sh : share, EX e : Z, EX next : val, EX lock : val, !!(readable_share sh /\ repable_signed e /\ e0 < e /\
if eq_dec e Int.max_signed then next = nullval else True) &&
data_at sh tnode (vint e, (next, lock)) p * ghost_var gsh1 p g *
EX g' : val, lock_inv sh lock (EX p : val, ghost_var gsh2 p g') *
if eq_dec e Int.max_signed then emp else atomic_loc sh next (fun v => |>R (e, g', v)).
Definition node gsh1 gsh2 := HORec (node_fun gsh1 gsh2).

Lemma node_isptr : forall gsh1 gsh2 e0 g p, node gsh1 gsh2 (e0, g, p) = !!isptr p && node gsh1 gsh2 (e0, g, p).
Hint Resolve node_isptr : saturate_local.