blob: d6f64527f93e691fd947a97f2440a24e7e3c7395 [file] [edit]
;; Interleaving stores
(module $Mem
(memory (export "shared") 1 1 shared)
)
(register "mem" $Mem)
(thread $T1 (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; x =rel 1
;; y =rel 2
(i32.atomic.store acqrel (i32.const 0) (i32.const 1))
(i32.atomic.store acqrel (i32.const 4) (i32.const 2))
)
)
(invoke "run")
)
(thread $T2 (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; y =rel 3
;; x =rel 4
(i32.atomic.store acqrel (i32.const 4) (i32.const 3))
(i32.atomic.store acqrel (i32.const 0) (i32.const 4))
)
)
(invoke "run")
)
(wait $T1)
(wait $T2)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "check") (result i32 i32)
;; read x, y
(i32.load (i32.const 0))
(i32.load (i32.const 4))
)
)
;; Nothing is synchronized so all 4 interleavings are possible.
;; x=1, y=3 is only possible with acqrel, while others are also possible with
;; seqcst.
(assert_return (invoke "check")
(either (i32.const 1) (i32.const 4))
(either (i32.const 2) (i32.const 3))
)
;; Critical section guarding an unordered memory access
(module $Mem
(memory (export "shared") 1 1 shared)
)
(register "mem" $Mem)
(thread $writer (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; payload =un 42
(i32.store (i32.const 4) (i32.const 42))
;; flag =rel 1 indicating that the payload was written
(i32.atomic.store acqrel (i32.const 0) (i32.const 1))
)
)
(invoke "run")
)
(thread $reader (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; observed_flag =acq flag
(i32.store (i32.const 8) (i32.atomic.load acqrel (i32.const 0)))
;; observed_payload =un payload
(i32.store (i32.const 12) (i32.load (i32.const 4)))
)
)
(invoke "run")
)
(wait $writer)
(wait $reader)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "check") (result i32)
;; If the flag is set, the payload must be set
;; If the flag is unset, the payload may or may not be set.
;; !observed_flag || observed_payload == 42
(i32.or
(i32.eqz (i32.load (i32.const 8)))
(i32.eq (i32.load (i32.const 12)) (i32.const 42))
)
)
)
(assert_return (invoke "check")
(i32.const 1)
)
;; Similar to above, critical section guarding a flag
(module $Mem
(memory (export "shared") 1 1 shared)
)
(register "mem" $Mem)
(thread $writer (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; payload =un 42
(i32.store (i32.const 4) (i32.const 42))
;; Release barrier
(atomic.fence acqrel)
;; flag indicating that the payload was written.
;; A relaxed ordering would be sufficient here but there's no such thing
;; at the moment.
;; In practice this and the fence together are redundant.
;; flag =rel 1
(i32.atomic.store acqrel (i32.const 0) (i32.const 1))
)
)
(invoke "run")
)
(thread $reader (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; A relaxed ordering would be sufficient here but we don't have it.
;; In practice this and the fence together are redundant.
;; observed_flag =acq flag
(i32.store (i32.const 8) (i32.atomic.load acqrel (i32.const 0)))
;; Acquire barrier
(atomic.fence acqrel)
;; observed_payload =un payload
(i32.store (i32.const 12) (i32.load (i32.const 4)))
)
)
(invoke "run")
)
(wait $writer)
(wait $reader)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "check") (result i32)
;; If the flag is set, the payload must be set
;; If the flag is unset, the payload may or may not be set.
;; !observed_flag || observed_payload == 42
(i32.or
(i32.eqz (i32.load (i32.const 8)))
(i32.eq (i32.load (i32.const 12)) (i32.const 42))
)
)
)
(assert_return (invoke "check")
(i32.const 1)
)
;; Spinlock
(module $Mem
;; Address 0 - lock
;; Address 4 - payload
(memory (export "shared") 1 1 shared)
)
(register "mem" $Mem)
;; Add 1 to the counter atomically
(thread $addOne (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func $lock
(loop $spin
;; Try to swap 0 with 1 at the lock address 0
(if (i32.eqz (i32.atomic.rmw.cmpxchg acqrel (i32.const 0) (i32.const 0) (i32.const 1)))
(then (return))
)
(pause)
(br $spin)
)
)
(func $unlock
;; lock =rel 0
(i32.atomic.store acqrel (i32.const 0) (i32.const 0))
)
(func (export "run")
(call $lock)
;; payload +=un 1
(i32.store (i32.const 4)
(i32.add (i32.load (i32.const 4)) (i32.const 1))
)
(call $unlock)
)
)
(invoke "run")
)
;; Add 10 to the counter atomically
(thread $addTen (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func $lock
(loop $spin
;; Try to swap 0 with 1 at the lock address 0
(if (i32.eqz (i32.atomic.rmw.cmpxchg acqrel (i32.const 0) (i32.const 0) (i32.const 1)))
(then (return))
)
(pause)
(br $spin)
)
)
(func $unlock
;; lock =rel 0
(i32.atomic.store acqrel (i32.const 0) (i32.const 0))
)
(func (export "run")
(call $lock)
;; payload +=un 10
(i32.store (i32.const 4)
(i32.add (i32.load (i32.const 4)) (i32.const 10))
)
(call $unlock)
)
)
(invoke "run")
)
(wait $addOne)
(wait $addTen)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "check") (result i32) (result i32)
;; read payload, lock
(i32.load (i32.const 4))
(i32.load (i32.const 0))
)
)
;; $addTen added 10 and $addOne added 1 atomically.
;; The lock was left unlocked at the end.
(assert_return (invoke "check")
(i32.const 11)
(i32.const 0)
)
;; independent reads of independent writes
(module $Mem
(memory (export "shared") 1 1 shared)
)
(register "mem" $Mem)
(thread $writerX (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; x =rel 1
(i32.atomic.store acqrel (i32.const 0) (i32.const 1))
)
)
(invoke "run")
)
(thread $writerY (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; y =rel 1
(i32.atomic.store acqrel (i32.const 4) (i32.const 1))
)
)
(invoke "run")
)
(thread $reader1 (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; x1 =acq x
;; y1 =acq y
(i32.store (i32.const 8) (i32.atomic.load acqrel (i32.const 0)))
(i32.store (i32.const 12) (i32.atomic.load acqrel (i32.const 4)))
)
)
(invoke "run")
)
(thread $reader2 (shared (module $Mem))
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "run")
;; y2 =acq y
;; x2 =acq x
(i32.store (i32.const 20) (i32.atomic.load acqrel (i32.const 4)))
(i32.store (i32.const 16) (i32.atomic.load acqrel (i32.const 0)))
)
)
(invoke "run")
)
(wait $writerX)
(wait $writerY)
(wait $reader1)
(wait $reader2)
(module
(memory (import "mem" "shared") 1 1 shared)
(func (export "check") (result i32 i32 i32 i32)
;; read x1, y1, x2, y2
(i32.load (i32.const 8))
(i32.load (i32.const 12))
(i32.load (i32.const 16))
(i32.load (i32.const 20))
)
)
;; All 4 combinations are possible
;; Under seqcst, x1=1, y1=0, x2=0, y2=1 isn't possible.
(assert_return (invoke "check")
(either (i32.const 0) (i32.const 1))
(either (i32.const 0) (i32.const 1))
(either (i32.const 0) (i32.const 1))
(either (i32.const 0) (i32.const 1))
)