(*+ Oracular semantics *)

(** Single-thread oracular semantics: each thread assumes that the
    oracle provides maps satisfying the invariants, but the thread
    also have to give back maps that satisfy those invariants. *)

Record globals := mkglobals
  { resinvs : nat -> pred (* resource invariant for each lock *) ;
    funspecs : string -> option pred (* precondition of each function *)
  }.(*+ Oracular semantics *)

(** Single-thread oracular semantics: each thread assumes that the
    oracle provides maps satisfying the invariants, but the thread
    also have to give back maps that satisfy those invariants. *)

Record globals := mkglobals
  { resinvs : nat -> pred (* resource invariant for each lock *) ;
    funspecs : string -> option pred (* precondition of each function *)
  }.(*+ Oracular semantics *)

(** Single-thread oracular semantics: each thread assumes that the
    oracle provides maps satisfying the invariants, but the thread
    also have to give back maps that satisfy those invariants. *)

Record globals := mkglobals
  { resinvs : nat -> pred (* resource invariant for each lock *) ;
    funspecs : string -> option pred (* precondition of each function *)
  }.(*+ Oracular semantics *)

(** Single-thread oracular semantics: each thread assumes that the
    oracle provides maps satisfying the invariants, but the thread
    also have to give back maps that satisfy those invariants. *)

Record globals := mkglobals
  { resinvs : nat -> pred (* resource invariant for each lock *) ;
    funspecs : string -> option pred (* precondition of each function *)
  }.Definition oracle := list map.Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).Inductive step : Rel (globals * oracle * map * C) :=
(*
  | step_halted G oracle m :
      (G, oracle, m, nil) --->
      (G, oracle, m, nil)
*)
  | step_core G oracle c c' m m' :
      corestep c m c' m' ->
      (G, oracle, m, normal c) --->
      (G, oracle, m', normal c')
  | step_release G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m' mlock m ->
      (G, oracle, m, release l p) --->
      (G, oracle, m', p)
  | step_acquire G oracle m mlock m' l p :
      mlock |= resinvs G l ->
      join m mlock m' ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, oracle, m', p)
  | step_acquire_wrong_oracle G oracle m mlock l p :
      ~(mlock |= resinvs G l /\ joins m mlock) ->
      (G, mlock :: oracle, m, acquire l p) --->
      (G, mlock :: oracle, m, acquire l p)
  | step_acquire_empty_oracle G m l p :
      (G, nil, m, acquire l p) --->
      (G, nil, m, acquire l p)
(*
  | step_spawn G PRE oracle m mspawned m' f p :
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      join m' mspawned m ->
      (G, oracle, m, spawn f :: p) --->
      (G, oracle, m', p)
*)
where
" c1 ---> c2 " := (step  c1 c2).(*+ Concurrent machine *)

(** Semantics of the collection of all threads. Thread and locked
    locks each have their own map. *)

Definition schedule := list nat.Definition threads := list (map * C).Definition lockpool := val -> option map.Definition functions := string -> option C.Definition config := (schedule * functions * globals * threads * lockpool)%type.(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(** reasons for having a list of threads rather than a function nat ->
option thread ? 1) It's a bit more deterministic and hence slighlty
easier to prove sound.  2) We can always allocate new threads. 3)
people prefer that, apparently *)

Inductive machine : Rel config :=
  | machine_outofschedule F G thd pool :
      (nil, F, G, thd, pool) ===>
      (nil, F, G, thd, pool)
  | machine_scheduleoutofbound i sch F G thd pool :
      nth thd i = None ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_core i sch F G thd c c' m m' pool :
      corestep c m c' m' ->
      nth thd i = Some (m, normal c) ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', normal c'), pool)
  | machine_release i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, release l p) ->
      mlock |= resinvs G l ->
      join m' mlock m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p), upd pool l (Some mlock))
  | machine_acquire_failure i sch F G thd m l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = None (* locked *) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_acquire_success i sch F G thd m mlock m' l p pool :
      nth thd i = Some (m, acquire l p) ->
      pool l = Some mlock (* unlocked *) ->
      join m mlock m' ->
      (i :: sch, F, G, thd, pool) ===>
      (i :: sch, F, G, nth_upd thd i (m', p), upd pool l None)
(*
  | machine_halted i sch F G thd m pool :
      nth thd i = Some (m, nil) ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, thd, pool)
  | machine_spawn i sch F G PRE thd m mspawned prog m' f p pool :
      nth thd i = Some (m, spawn f :: p) ->
      funspecs G f = Some PRE ->
      mspawned |= PRE ->
      F f = Some prog ->
      join m' mspawned m ->
      (i :: sch, F, G, thd, pool) ===>
      (sch, F, G, nth_upd thd i (m', p) ++ (mspawned, prog) :: nil, pool)
*)
where
" c1 ===> c2 " := (machine c1 c2).(*+ Collection of disjoint maps *)

(** For the proof of composition of safety, we need to maintain the
    invariants "all the maps are disjoint*" which is why we need this
    section.

  * pairwise-disjointness is equivalent to arbitrary disjointness
    because the permissions are "set-theoretical" *)

Definition selfjoins {A} (maps : A -> map) :=
  forall i j, i <> j -> joins (maps i) (maps j).Definition sameperms (m n : map) := forall x, m x = None <-> n x = None.

(* Specifications: *)
Lemma sameperms_refl m : sameperms m m.
Lemma sameperms_sym m n : sameperms m n -> sameperms n m.
Lemma sameperms_trans m n o : sameperms m n -> sameperms n o -> sameperms m o.
Lemma sameperms_updatemap m x e m' : updatemap m x e = Some m' -> sameperms m m'.
Lemma sameperms_joins m m' n : sameperms m m' -> joins m n -> joins m' n.
Lemma selfjoins_sameperms_compat {A} maps1 maps2 : (forall i:A, sameperms (maps1 i) (maps2 i)) -> selfjoins maps1 -> selfjoins maps2.