Skip to content

Commit cbb6edc

Browse files
authored
Allow atomic operations on unshared memories (#147)
As discussed in the CG on November 12, 2019 and in #144. Specifically, all atomic accesses are now allowed to validate and execute normally on unshared memories and wait operations trap when used with unshared memories. The PR updates Overview.md and makes a best-effort attempt at updating the spec, making no changes when there is already a TODO for updating spec text. It also adds new TODO comments to the reference interpreter where changes will have to be made.
1 parent 4b323b9 commit cbb6edc

File tree

5 files changed

+32
-47
lines changed

5 files changed

+32
-47
lines changed

document/core/valid/instructions.rst

+10-30
Original file line numberDiff line numberDiff line change
@@ -440,15 +440,13 @@ Atomic Memory Instructions
440440

441441
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
442442

443-
* The sharedness :math:`\share` must be |MSHARED|.
444-
445443
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to :math:`4`.
446444

447445
* Then the instruction is valid with type :math:`[\I32~\I64] \to [\I64]`.
448446

449447
.. math::
450448
\frac{
451-
C.\CMEMS[0] = \limits~\MSHARED
449+
C.\CMEMS[0] = \memtype
452450
\qquad
453451
2^{\memarg.\ALIGN} = 4
454452
}{
@@ -464,15 +462,13 @@ Atomic Memory Instructions
464462

465463
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
466464

467-
* The sharedness :math:`\share` must be |MSHARED|.
468-
469465
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to the :ref:`width <syntax-valtype>` of :math:`t` divided by :math:`8`.
470466

471467
* Then the instruction is valid with type :math:`[\I32~t~\I64] \to [\I32]`.
472468

473469
.. math::
474470
\frac{
475-
C.\CMEMS[0] = \limits~\MSHARED
471+
C.\CMEMS[0] = \memtype
476472
\qquad
477473
2^{\memarg.\ALIGN} = |t|/8
478474
}{
@@ -488,15 +484,13 @@ Atomic Memory Instructions
488484

489485
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
490486

491-
* The sharedness :math:`\share` must be |MSHARED|.
492-
493487
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to the :ref:`width <syntax-valtype>` of :math:`t` divided by :math:`8`.
494488

495489
* Then the instruction is valid with type :math:`[\I32] \to [t]`.
496490

497491
.. math::
498492
\frac{
499-
C.\CMEMS[0] = \limits~\MSHARED
493+
C.\CMEMS[0] = \memtype
500494
\qquad
501495
2^{\memarg.\ALIGN} = |t|/8
502496
}{
@@ -513,15 +507,13 @@ Atomic Memory Instructions
513507

514508
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
515509

516-
* The sharedness :math:`\share` must be |MSHARED|.
517-
518510
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to :math:`N/8`.
519511

520512
* Then the instruction is valid with type :math:`[\I32] \to [t]`.
521513

522514
.. math::
523515
\frac{
524-
C.\CMEMS[0] = \limits~\MSHARED
516+
C.\CMEMS[0] = \memtype
525517
\qquad
526518
2^{\memarg.\ALIGN} = N/8
527519
}{
@@ -538,15 +530,13 @@ Atomic Memory Instructions
538530

539531
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
540532

541-
* The sharedness :math:`\share` must be |MSHARED|.
542-
543533
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to the :ref:`width <syntax-valtype>` of :math:`t` divided by :math:`8`.
544534

545535
* Then the instruction is valid with type :math:`[\I32~t] \to []`.
546536

547537
.. math::
548538
\frac{
549-
C.\CMEMS[0] = \limits~\MSHARED
539+
C.\CMEMS[0] = \memtype
550540
\qquad
551541
2^{\memarg.\ALIGN} = |t|/8
552542
}{
@@ -563,15 +553,13 @@ Atomic Memory Instructions
563553

564554
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
565555

566-
* The sharedness :math:`\share` must be |MSHARED|.
567-
568556
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to :math:`N/8`.
569557

570558
* Then the instruction is valid with type :math:`[\I32~t] \to []`.
571559

572560
.. math::
573561
\frac{
574-
C.\CMEMS[0] = \limits~\MSHARED
562+
C.\CMEMS[0] = \memtype
575563
\qquad
576564
2^{\memarg.\ALIGN} = N/8
577565
}{
@@ -588,15 +576,13 @@ Atomic Memory Instructions
588576

589577
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
590578

591-
* The sharedness :math:`\share` must be |MSHARED|.
592-
593579
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to the :ref:`width <syntax-valtype>` of :math:`t` divided by :math:`8`.
594580

595581
* Then the instruction is valid with type :math:`[\I32~t] \to [t]`.
596582

597583
.. math::
598584
\frac{
599-
C.\CMEMS[0] = \limits~\MSHARED
585+
C.\CMEMS[0] = \memtype
600586
\qquad
601587
2^{\memarg.\ALIGN} = |t|/8
602588
}{
@@ -612,15 +598,13 @@ Atomic Memory Instructions
612598

613599
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
614600

615-
* The sharedness :math:`\share` must be |MSHARED|.
616-
617601
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to :math:`N/8`.
618602

619603
* Then the instruction is valid with type :math:`[\I32~t] \to [t]`.
620604

621605
.. math::
622606
\frac{
623-
C.\CMEMS[0] = \limits~\MSHARED
607+
C.\CMEMS[0] = \memtype
624608
\qquad
625609
2^{\memarg.\ALIGN} = N/8
626610
}{
@@ -636,15 +620,13 @@ Atomic Memory Instructions
636620

637621
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
638622

639-
* The sharedness :math:`\share` must be |MSHARED|.
640-
641623
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to the :ref:`width <syntax-valtype>` of :math:`t` divided by :math:`8`.
642624

643625
* Then the instruction is valid with type :math:`[\I32~t~t] \to [t]`.
644626

645627
.. math::
646628
\frac{
647-
C.\CMEMS[0] = \limits~\MSHARED
629+
C.\CMEMS[0] = \memtype
648630
\qquad
649631
2^{\memarg.\ALIGN} = |t|/8
650632
}{
@@ -660,15 +642,13 @@ Atomic Memory Instructions
660642

661643
* Let :math:`\limits~\share` be the :ref:`memory type <syntax-memtype>` :math:`C.\CMEMS[0]`.
662644

663-
* The sharedness :math:`\share` must be |MSHARED|.
664-
665645
* The alignment :math:`2^{\memarg.\ALIGN}` must be equal to :math:`N/8`.
666646

667647
* Then the instruction is valid with type :math:`[\I32~t~t] \to [t]`.
668648

669649
.. math::
670650
\frac{
671-
C.\CMEMS[0] = \limits~\MSHARED
651+
C.\CMEMS[0] = \memtype
672652
\qquad
673653
2^{\memarg.\ALIGN} = N/8
674654
}{

interpreter/exec/eval.ml

+1
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,7 @@ let rec step (c : config) : config =
277277
with exn -> vs', [Trapping (memory_error e.at exn) @@ e.at]);
278278

279279
| AtomicWait {offset; ty; sz; _}, I64 timeout :: ve :: I32 i :: vs' ->
280+
(* TODO: Trap if memory is not shared *)
280281
let mem = memory frame.inst (0l @@ e.at) in
281282
let addr = I64_convert.extend_i32_u i in
282283
(try

interpreter/valid/valid.ml

+1
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ let type_cvtop at = function
137137

138138
(* Expressions *)
139139

140+
(* TODO: Remove sharability requirements *)
140141
let check_memop (c : context) (memop : 'a memop) (sh: sharability option)
141142
get_sz at =
142143
let MemoryType (_, shared) = memory c (0l @@ at) in

proposals/threads/Globals.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
12
# Import/Export mutable globals proposal
23

34
This has been separated into

proposals/threads/Overview.md

+19-17
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,8 @@ This has been separated into
228228
## Atomic Memory Accesses
229229

230230
Atomic memory accesses are separated into three categories, load/store,
231-
read-modify-write, and compare-exchange. All atomic memory accesses require a
232-
shared linear memory. Attempting to use atomic access operators on non-shared
233-
linear memory is a validation error.
231+
read-modify-write, and compare-exchange. All atomic memory accesses can be
232+
performed on both shared and unshared linear memories.
234233

235234
Currently all atomic memory access instructions are [sequentially consistent][].
236235
Instructions with other memory orderings may be provided in the future.
@@ -340,12 +339,12 @@ has any other value than the natural alignment for that access size.
340339
## Wait and Notify operators
341340

342341
The notify and wait operators are optimizations over busy-waiting for a value
343-
to change. It is a validation error to use these operators on non-shared linear
344-
memory. The operators have sequentially consistent ordering.
342+
to change. The operators have sequentially consistent ordering.
345343

346344
Both notify and wait operators trap if the effective address of either operator
347-
is misaligned or out-of-bounds. The wait operators require an alignment of
348-
their memory access size. The notify operator requires an alignment of 32 bits.
345+
is misaligned or out-of-bounds. Wait operators additionally trap if used on an
346+
unshared linear memory. The wait operators require an alignment of their memory
347+
access size. The notify operator requires an alignment of 32 bits.
349348

350349
For the web embedding, the agent can also be suspended or woken via the
351350
[`Atomics.wait`][] and [`Atomics.notify`][] functions respectively. An agent
@@ -373,14 +372,15 @@ and a relative timeout in nanoseconds as an `i64`. The return value is `0`,
373372
| `1` | "not-equal", the loaded value did not match the expected value |
374373
| `2` | "timed-out", not woken before timeout expired |
375374

376-
The wait operation begins by performing an atomic load from the given address.
377-
If the loaded value is not equal to the expected value, the operator returns 1
378-
("not-equal"). If the values are equal, the agent is suspended. If the agent
379-
is woken, the wait operator returns 0 ("ok"). If the timeout expires before
380-
another agent notifies this one, this operator returns 2 ("timed-out"). Note that
381-
when the agent is suspended, it will not be [spuriously woken](https://en.wikipedia.org/wiki/Spurious_wakeup).
382-
The agent is only woken by `atomic.notify` (or [`Atomics.notify`][] in the web
383-
embedding).
375+
If the linear memory is unshared, the wait operation traps. Otherwise, the wait
376+
operation begins by performing an atomic load from the given address. If the
377+
loaded value is not equal to the expected value, the operator returns 1
378+
("not-equal"). If the values are equal, the agent is suspended. If the agent is
379+
woken, the wait operator returns 0 ("ok"). If the timeout expires before another
380+
agent notifies this one, this operator returns 2 ("timed-out"). Note that when
381+
the agent is suspended, it will not be [spuriously
382+
woken](https://en.wikipedia.org/wiki/Spurious_wakeup). The agent is only woken
383+
by `atomic.notify` (or [`Atomics.notify`][] in the web embedding).
384384

385385
When an agent is suspended, if the number of waiters (including this one) is
386386
equal to 2<sup>32</sup>, then trap.
@@ -424,7 +424,9 @@ no `Int64Array` type, and an ECMAScript `Number` cannot represent all values of
424424
The notify operator takes two operands: an address operand and a count as an
425425
unsigned `i32`. The operation will notify as many waiters as are waiting on the
426426
same effective address, up to the maximum as specified by `count`. The operator
427-
returns the number of waiters that were woken as an unsigned `i32`.
427+
returns the number of waiters that were woken as an unsigned `i32`. Note that if
428+
the notify operator is used with an unshared linear memory, the number of
429+
waiters will always be zero.
428430

429431
* `atomic.notify`: notify `count` threads waiting on the given address via `i32.atomic.wait` or `i64.atomic.wait`
430432

@@ -441,7 +443,7 @@ For the web embedding, `atomic.notify` is equivalent in behavior to executing th
441443

442444
The fence operator, `atomic.fence`, takes no operands, and returns nothing. It is intended to preserve the synchronization guarantees of the [fence operators of higher-level languages](https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence).
443445

444-
Unlike other atomic operators, `atomic.fence` does not target a particular linear memory. It may occur in modules which declare no memory, or a non-shared memory, without causing a validation error.
446+
Unlike other atomic operators, `atomic.fence` does not target a particular linear memory. It may occur in modules which declare no memory without causing a validation error.
445447

446448
## [JavaScript API][] changes
447449

0 commit comments

Comments
 (0)