[jmm-dev] DRF theorem for event structures model of relaxed memory

Alan Jeffrey ajeffrey at bell-labs.com
Wed Mar 26 17:41:39 UTC 2014


Another update on where we are with modeling relaxed memory...

The DRF theorem is back! I proved the DRF theorem for the event 
structures model. Lots of gory details below.

We have an event structures model of a simple imperative programming 
language (good enough for the causality test cases).

Next up... checking that the programming language satisfies the 
preconditions of the DRF theorem, checking that the causality test cases 
have their expected behavior, and making sure we're doing the right 
thing wrt synchronization actions.

A.

DRF Theorem
-----------

 From Winskel, a prime event structure with labels Sigma is a quadruple 
(E,<=,#,l) where:

* E is a set of events
* <= is a partial order
* # is a down-closed symmetric binary relation
* l is a function from E to Sigma

We will write e in HB(d) for e < d and e in HA(d) for e > d.

An event structure is pointed when E has a least element init.

New here, a memory model alphabet is a set Sigma together with:

* An element initialize of Sigma
* A subset Sync of Sigma
* Binary relations RWJ subseteq RWC on Sigma such that
* RWC is transitive
* For any a, (initialize,a) in RWC

[The idea is that RWJ is the "read-write justification" relation, for 
example (R x=v, W x=v) in RWJ, and RWC is the "read-write conflict" 
relation, for example (R x=v, W x=w) in RWC.]

 From RWC we can define WWC and DC (data conflict):

   (a,b) in WWC whenever there exists (c,a) in RWC and (c,b) in RWC
   (a,b) in DC whenever (a,b) in RWC or (b,a) in RWC or (a,b) in WWC

A memory model event structure is a pointed event structure whose labels 
form a memory model alphabet, such that l(init) = initialize.

For any MMES, we can lift RWJ, RWC, WWC and DC from Sigma to E, for example:

   (d,e) in RWJ whenever (d,e) not in # and (l(d),l(e)) in RWJ

Define past-time LTL over E* as per usual, in particular (writing <= on 
strings for prefix order):

   epsilon in wkprev(phi) always
   se in wkprev(phi) whenever s in phi

   s in always phi whenever for all t <= s we have t in phi

   s in eventually phi whenever there exists t <= s s.t. t in phi

   s in phi since psi whenever there exists u <= s s.t. u in psi
     and for all u < t <= s we have t in phi

and one extra modality (writing <~ for non-contiguous substring order):

   s in extended(phi) whenever there exists t ~> s s.t. t in phi

For example, propositional LTL is enough to define being a configuration 
of an ES (as per Winskel though on sequences rather than sets):

   HBClosed = forall e . forall d < e . (e => eventually(d))

   ConflictFree = forall e . (e => always(not(#(e))))

   RepeatFree = forall e . (e => always(not(e)))

   Configuration = HBClosed and ConflictFree and RepeatFree

and the memory model:

   DRF = always (forall e . e => always(DC(e) => HB(e)))

   Sequential = always (forall e . exists d in RWJ(e) .
     e => wkprev(not RWC(e) since d)

   SConfiguration = Configuration and Sequential

   Relaxed = always (forall e . exists d in RWJ(e) .
     e => wkprev(not (HB(e) and HA(d) and RWC(e)) since d)

   RConfiguration = Configuration and
     extended (HBClosed and RepeatFree and Relaxed)

The idea is that a configuration is an rconfiguration (that is, allowed 
by the relaxed memory model) when it can be embedded in a <-closed, 
repeat-free (but *not* conflict-free) sequence of actions such that 
every event has a visible read-write justifier.) For example the program:

   thread 1: r=x; y=r
   thread 2: r=y; x=1

has event structure with order:

   init <= (R x=1) <= (W y=1)
   init <= (R x=0) <= (W y=0)
   init <= (R y=1) <= (W x=1)
   init <= (R y=0) <= (W x=1)

and conflict relation generated by:

   (R x=1) # (R x=0)
   (R y=1) # (R y=0)

The configuration:

   (init) (R y=1) (W x=1) (R x=1) (W x=1)

is an rconfiguration because it can be embedded in:

   (init) (R x=0) (W x=1) (R y=1) (W x=1) (R x=1) (W x=1)

which is relaxed (but note, not conflict-free as it contains (R x=0) as 
well as (R x=1)).

The model doesn't admit the canonical OOTA execution, since the program:

   thread 1: r=x; y=r
   thread 2: r=y; x=r

has event structure with order:

   init <= (R x=1) <= (W y=1)
   init <= (R x=0) <= (W y=0)
   init <= (R y=1) <= (W x=1)
   init <= (R y=0) <= (W x=0)

and conflict relation generated by:

   (R x=1) # (R x=0)
   (R y=1) # (R y=0)

but there is no extension of the configuration:

   (init) (R y=1) (W x=1) (R x=1) (W x=1)

which makes it an rconfiguration, for example the candidate:

   (init) (R x=0) (W x=0) (R y=1) (W x=1) (R x=1) (W x=1)

has an unjustified (R y=1) event.

To state the DRF theorem, we need one more thing which is the concept of 
a "read-write-enabled" event structure. Informally the idea is that if a 
program can perform (R x=1) then it can also perform (R x=0). 
Formalizing this, we define an equivalence class ~ on events such that 
if (d ~ e) then:

   (c,d) in RWC iff (c,e) in RWC
   (d,c) in RWC iff (e,c) in RWC
   (c,d) in HB  iff (c,e) in HB
   either d=e or d#e
   if d#c then either e<=c or e#c
   if e#c then either d<=c or d#c

The read-write-enabled events are the ones

   (d,e) in RWE iff there exists c~d s.t. (c,e) in RWJ

For example (R x=1, W x=0) in RWE whenever we can find (R x=0)~(R x=1) 
such that (R x=0, W x=0) in RWJ. The additional requirement it then that 
every read-write-conflict is read-write-enabled, and the DRF theorem is 
then:

   If (RWC subseteq RWE)
   and (SConfiguration subseteq DRF)
   then (RConfiguration subseteq SConfiguration)



More information about the jmm-dev mailing list