├── .gitignore ├── COMPILER.md ├── CONTRIBUTORS.md ├── DEMOS.md ├── DISCUSSION.md ├── FAQ.md ├── LICENSE ├── MACHINES.md ├── MEMMODEL_CONSTRAINTS.md ├── README.md ├── TUTORIAL.md ├── asmjs ├── asmjs_shmem.html ├── asmjs_spec.html ├── emu.css └── emu.js ├── dom ├── dom_shmem.html ├── dom_spec.html ├── emu.css └── emu.js ├── format.sh ├── historical ├── README.md ├── SharedMemoryAtomicsLocks-asm.js-additions.odt ├── SharedMemoryAtomicsLocks-asm.js-additions.pdf ├── Spec_JavaScriptSharedMemoryAtomicsandLocks.odt ├── Spec_JavaScriptSharedMemoryAtomicsandLocks.pdf ├── Synchronic.odt └── Synchronic.pdf ├── issues ├── FutexWaitOnMainThread.md ├── MemoryModelConsequences.md ├── TimingAttack.md └── WhyNotJustAsmOrWasm.md ├── tc39 ├── README.md ├── agents-mar-2016.odp ├── agents-mar-2016.pdf ├── clippings.html ├── emu.css ├── emu.js ├── presentation-jan-2016.odp ├── presentation-jan-2016.pdf ├── presentation-nov-2016.pdf ├── presentation-nov-2016.tex ├── presentation-sept-2015.odp ├── presentation-sept-2015.pdf ├── sharedmem-jul-2016.odp ├── sharedmem-jul-2016.pdf ├── sharedmem-mar-2016.odp ├── sharedmem-mar-2016.pdf ├── shmem.html └── spec.html ├── test-html ├── LICENSE.md ├── external │ ├── README.md │ ├── serviceWorkerNotSharing_master.js │ ├── serviceWorkerNotSharing_worker.js │ └── test_serviceWorkerNotSharing.html ├── futexInterrupt-worker.js ├── futexMainWakeup-worker.js ├── futexTimeout_slave.js ├── futexWakeInInterrupt-worker.js ├── harness.js ├── index.html ├── sharedMemCriticalSection_defs.js ├── sharedMemCriticalSection_master.js ├── sharedMemCriticalSection_slave.js ├── sharedMemSimpleMutex.js ├── sharedTypedArrayClone-worker.js ├── sharing_sharedworker_and_nested_worker_child.js ├── sharing_sharedworker_and_nested_worker_parent.js ├── sharing_window_and_sharedworker_master.js ├── sharing_window_and_sharedworker_worker.js ├── sharing_window_and_worker.js ├── sharing_worker_and_nested_worker_child.js ├── sharing_worker_and_nested_worker_parent.js ├── test_futexInterrupt.html ├── test_futexMainTimeout.html ├── test_futexMainWakeup.html ├── test_futexSlowscript.html ├── test_futexTimeout.html ├── test_futexWakeInInterrupt.html ├── test_serviceWorkerNotSharing.html ├── test_sharedMemCriticalSection.html ├── test_sharedTypedArrayClone.html ├── test_sharing_sharedworker_and_nested_worker.html ├── test_sharing_window_and_sharedworker.html ├── test_sharing_window_and_worker.html ├── test_sharing_worker_and_nested_worker.html └── worker-harness.js └── test262 ├── LICENSE.md ├── README.md ├── agenttest_spidermonkey.js ├── atomics-on-good-arrays.js ├── atomics-on-nonshared-int-arrays.js ├── atomics-on-other-stuff.js ├── atomics-on-shared-nonint-arrays.js ├── atomics-range.js ├── dataview-props.js ├── futex-misc.js ├── futex-on-nonshared-int-arrays.js ├── futex-on-other-stuff.js ├── futex-on-shared-nonint-arrays.js ├── futex-on-shared-nonint32-arrays.js ├── futex-range.js ├── futex-wait-return.js ├── harness.js ├── is-lock-free-corner-cases.js ├── is-lock-free-good.js ├── runner.html ├── shared-array-buffer-conversion.js ├── shared-array-buffer-instance-props.js ├── shared-array-buffer-props.js ├── shell-runner ├── typedarray-props-range.js ├── typedarray-props.js └── wait-wake.js /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | -------------------------------------------------------------------------------- /COMPILER.md: -------------------------------------------------------------------------------- 1 | The memory model provides precise semantics for shared memory events, but it does so without specifying what the agent-order relation of an agent in an execution may be. Yet the agent-order may plausibly vary as a result of the transformations performed by the ECMAScript implementation and the hardware the program is running on. That is obviously the case if were to consider a single agent running in isolation. In the single-agent case, all semantics-preserving transformations are allowed, but, being semantics-preserving, they are not observable to the agent. 2 | 3 | The question arises as to which transformations that are allowed in a multi-agent setting, where transformations that affect shared memory writes are observable by other agents. An agent that writes 0 to `x` and then writes 1 to `y` can be transformed in a single-agent setting to one that writes 1 to `y` and then writes 0 to `x`. But is that a legal transformation if those variables are in shared memory and another agent might observe the order of writes? The transformation would affect the agent-order locally and the happens-before relation globally. And does the answer depend on whether `x` and `y` are written with "SeqCst" operations or not? 4 | 5 | It is desirable to allow most program transformations that are valid in a single-agent setting in a multi-agent setting, to ensure that the performance of each agent in a multi-agent program is as good as it would be in a single-agent setting. (There seems to be broad agreement in the programming language design community that that is the right trade-off.) But "most" does not mean "all". In the example above, if `x` or `y` (or both) were intended to be "SeqCst" writes, and the writes were reordered, not only might happens-before be affected, but also synchronizes-with and memory-order, and a program that before transformations was data-race-free and sequentially consistent might no longer be so. In contrast, if `x` and `y` were both "Unordered" then in a data-race-free program the change in write order would not be observable at all. 6 | 7 | Furthermore, consider an agent that writes first 0 to `x` and then (significantly) later writes 1 to `x` (both writes "Unordered"). In a single-agent setting the first write can be removed provided that `x` will not be read until the second write has happened. In a multi-agent setting, another agent waiting for the value of `x` to become 0 might never stop waiting if the first write is removed. 8 | 9 | Frequently these transformations are hard to judge. Can an "Unordered" load be moved out of a loop? Even if the loop's termination condition depends on the loaded value? The load is necessarily racy, but the memory model gives some meaning to races; can we depend on that meaning here? 10 | 11 | With that background, we outline some rules about program transformations that are intended to be taken as normative but which are likely not exhaustive. These rules are "above" or "prior to" the memory model; they are intended to apply to program transformations that precede the introductions of the events that make up the agent-order. 12 | 13 | Let an "agent-order slice" be the subset of the agent-order pertaining to a single agent. 14 | 15 | The main rule is that _any transformation of an agent-order slice that is valid in the absence of shared memory is valid in the presence of shared memory._ The following exceptions to the main rule then apply: 16 | 17 | * _Atomics are carved in stone:_ Program transformations must not cause the "SeqCst" events in an agent-order slice to be reordered with its "Unordered" operations, nor its "SeqCst" operations to be reordered with each other, nor may a program transformation remove a "SeqCst" operation from the agent-order. (In practice, the prohibition on reorderings forces a compiler to assume that every "SeqCst" operation is included in the final memory-order, which it would usually have to assume anyway in the absence of inter-agent program analysis. It also forces the compiler to assume that every call where the callee's effects on the memory-order are unknown may contain "SeqCst" operations.) 18 | 19 | * _Reads must be stable:_ Any given shared memory read must only observe a single value. (For example, if what is semantically a single read in the program is executed multiple times then the program is subsequently allowed to observe only one of the values read. A transformation known as rematerialization can violate this rule.) 20 | 21 | * _Writes must be stable:_ All observable writes to shared memory must follow from program semantics. (For example, a transformation may not introduce certain observable writes, such as by using read-modify-write operations on a larger location to write a smaller datum, writing a value to memory that the program could not have written, or writing a just-read value back to the location it was read from, if that location could have been overwritten by another agent after the read.) 22 | 23 | * _Writes must become visible:_ If an agent writes to a shared memory location non-atomically then an "Unordered" event to the location must be inserted in the agent-order slice before the next "SeqCst" event is introduced by the agent, or before the agent's job ends if there is no next "SeqCst" event. (For example, writes may be moved and coalesced and sometimes reordered between two "SeqCst" operations, but the transformation may not remove every write that updates a location; some write must be preserved.) 24 | 25 | (Spec draft note) It's probably desirable not to have the "Atomics are carved in stone" rule, but given that we try to give meaning to races, this rule gives us somewhere to stand in terms of the kinds of reorderings we can expect. 26 | 27 | (Spec draft note) Some of the rules overlap in part with the rules of the memory model, but that seems OK: the effect of these rules is not just to add constraints beyond the memory model but equally to free the compiler from a having to interpret the memory model literally in terms of the events that have to be introduced. 28 | 29 | The rules apply only to shared memory accesses, so an implementation that knows the program is not accessing shared memory will not be affected by them. In practice, the optimizations that are affected by the rules are of limited value, and a practical implementation could avoid those optimizations even for non-shared memory without significant impact. 30 | 31 | Examples of transformations that remain valid (so long as they don't otherwise violate the memory model) are: merging multiple non-atomic reads from the same location, reordering non-atomic reads, introducing speculative non-atomic reads, merging multiple non-atomic writes to the same location, reordering non-atomic writes to different locations, and yes, hoisting non-atomic reads out of loops even if that affects termination. Note in general that aliased TypedArrays make it hard to prove that locations are different. 32 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | Here are the contributors to this spec in alphabetical order. 2 | The following list is from memory and a scrub of various email threads and the issues on this repo. 3 | The list may be incomplete: sadly the attributions on the original Google Doc comments were stripped 4 | when the document was exported for archiving. Please file PRs against this file 5 | (ideally with some kind of reference to where the name appears) for missing names. 6 | 7 | * Anne van Kesteren 8 | * Ben Smith 9 | * Brad Nelson 10 | * Brian Demsky 11 | * Dan Gohman 12 | * Ecma-TC39 "ECMAScript" standards committee members 13 | * Filip Pizlo 14 | * Grant Galitz 15 | * Hans Boehm 16 | * Jeffrey Yasskin 17 | * JF Bastien 18 | * Jukka Jylänki 19 | * Liam Wilson 20 | * Luke Wagner 21 | * Mark Batty 22 | * Nick Bray 23 | * Olivier Giroux 24 | * PeteX 25 | * Sean Stangl 26 | -------------------------------------------------------------------------------- /DEMOS.md: -------------------------------------------------------------------------------- 1 | # Demos 2 | 3 | There are many demonstration programs, examples, and test cases in the repo [https://github.com/lars-t-hansen/parlib-simple](https://github.com/lars-t-hansen/parlib-simple). The README.md file in that repo contains a general roadmap. Below are also some suggestions about where to start. 4 | 5 | ## Where to start 6 | 7 | The demos and test cases in parlib-simple are written at varying abstraction levels, and in particular, many of them appear a little messy because they manage memory manually. The following are illustrative. 8 | 9 | The easiest place to start might be src/buffer.js, which implements a classical bounded buffer using the lock and condition variables implemented in src/lock.js. These are then tied together in an example program in test/test-buffer.html, where several workers insert values into a buffer and the main thread extracts those values. 10 | 11 | A more elaborate take on the same idea is in src/intqueue.js, which is a queue of bundles of integers, built on the Synchronic abstraction in src/synchronic.js and also on a simple memory allocator. IntQueue is then used, along with value marshaling code in src/marshaler.js, to build a *synchronous inter-worker communication channel* in src/channel.js. The test program in test/test-sendmsg.html uses this system to send values back and forth between agents. 12 | 13 | Finally, the data-parallel framework in src/par.js provides a simple perform-work-in-parallel abstraction, with automatic work queue management and marshaling of arguments. Simple demonstrations of this facility are in demo/mandelbrot-animation2 and demo/mandelbrot-animation-simd. 14 | 15 | ## Web applications using shared memory 16 | 17 | * http://statebuilder.com/ - 4X strategy game, built with custom C++ engine, ported to the web using Emscripten. 18 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # Frequently asked questions 2 | 3 | ## My racy program does not do what I expect it to do 4 | 5 | ### The problem 6 | 7 | Consider two threads where one is busy-waiting on the other: 8 | 9 | ```js 10 | // Thread 1 // Thread 2 11 | while (mem[n] == 0) { ... 12 | /* do nothing */ mem[n] = 1 13 | } ... 14 | print("Hi") 15 | ``` 16 | 17 | The programmer's intent is clear: the loop in Thread 1 18 | waits until a flag is set, and eventually Thread 2 sets the flag, 19 | signaling for Thread 1 to leave the loop. 20 | 21 | Thread 1 may never print "Hi", or it may only sometimes print "Hi" 22 | and sometimes not. (When it does not it will loop forever.) The reason 23 | is that the read in the loop does not synchronize with the write, and may 24 | therefore be lifted out of the loop by the compiler, like this: 25 | 26 | ```js 27 | // Thread 1 28 | let temp = mem[n]; 29 | while (temp == 0) { 30 | /* do nothing */ 31 | } 32 | print("Hi") 33 | ``` 34 | 35 | If the write by Thread 2 is visible to Thread 1 at the time Thread 1 performs the 36 | read then the loop is skipped, but if the write comes later then it is not seen at all 37 | and the loop runs forever. 38 | 39 | The Firefox JavaScript compiler performs that optimization, so the program 40 | will run fine when it runs in the interpreter but will stop working 41 | once the code becomes compiled. 42 | 43 | The solution is to use proper synchronization: 44 | 45 | ```js 46 | // Thread 1 // Thread 2 47 | while (Atomics.load(mem, n) == 0) { ... 48 | /* do nothing */ Atomics.store(mem, n, 1); 49 | } ... 50 | print("Hi") 51 | ``` 52 | 53 | ### Discussion 54 | 55 | Many programmers are upset when they encounter this problem because they feel that the compiler should not be performing that type of optimization in shared memory. 56 | 57 | However, experience has shown that it is desirable for compilers and hardware to be able to perform many of the same optimizations on shared-memory code as they do on non-shared-memory code. This requires programmers to insert synchronization code in their programs; the synchronization serves as a directive to the compiler and hardware that some optimizations must be disabled around the synchronization. Synchronization is usually more expensive than "plain" code, but a language that allows full optimization between synchronization points provides better performance than a language that disables many optimizations everywhere in order to have "expected" semantics. 58 | 59 | In fact, on most types of hardware it would be quite expensive to provide "expected" semantics, because all memory operations on shared memory would have to be completely ordered. Hardware and compiler optimizations frequently move, rearrange, or coalesce memory operations; some popular hardware such as ARM CPUs does so particularly aggressively. Enforcing the ordering would require the insertion of expensive barrier instructions between memory instructions, greatly slowing down programs. 60 | 61 | (As pretty much the only reason to be writing parallel code is higher performance, a slow parallel language is not very interesting for practical use.) 62 | 63 | There is a longer discussion in [Issue #40](https://github.com/tc39/ecmascript_sharedmem/issues/40) with links to papers and presentations for those interested in more background. 64 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License, version 2.0 2 | 3 | 1. Definitions 4 | 5 | 1.1. "Contributor" 6 | 7 | means each individual or legal entity that creates, contributes to the 8 | creation of, or owns Covered Software. 9 | 10 | 1.2. "Contributor Version" 11 | 12 | means the combination of the Contributions of others (if any) used by a 13 | Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | 17 | means Covered Software of a particular Contributor. 18 | 19 | 1.4. "Covered Software" 20 | 21 | means Source Code Form to which the initial Contributor has attached the 22 | notice in Exhibit A, the Executable Form of such Source Code Form, and 23 | Modifications of such Source Code Form, in each case including portions 24 | thereof. 25 | 26 | 1.5. "Incompatible With Secondary Licenses" 27 | means 28 | 29 | a. that the initial Contributor has attached the notice described in 30 | Exhibit B to the Covered Software; or 31 | 32 | b. that the Covered Software was made available under the terms of 33 | version 1.1 or earlier of the License, but not also under the terms of 34 | a Secondary License. 35 | 36 | 1.6. "Executable Form" 37 | 38 | means any form of the work other than Source Code Form. 39 | 40 | 1.7. "Larger Work" 41 | 42 | means a work that combines Covered Software with other material, in a 43 | separate file or files, that is not Covered Software. 44 | 45 | 1.8. "License" 46 | 47 | means this document. 48 | 49 | 1.9. "Licensable" 50 | 51 | means having the right to grant, to the maximum extent possible, whether 52 | at the time of the initial grant or subsequently, any and all of the 53 | rights conveyed by this License. 54 | 55 | 1.10. "Modifications" 56 | 57 | means any of the following: 58 | 59 | a. any file in Source Code Form that results from an addition to, 60 | deletion from, or modification of the contents of Covered Software; or 61 | 62 | b. any new file in Source Code Form that contains any Covered Software. 63 | 64 | 1.11. "Patent Claims" of a Contributor 65 | 66 | means any patent claim(s), including without limitation, method, 67 | process, and apparatus claims, in any patent Licensable by such 68 | Contributor that would be infringed, but for the grant of the License, 69 | by the making, using, selling, offering for sale, having made, import, 70 | or transfer of either its Contributions or its Contributor Version. 71 | 72 | 1.12. "Secondary License" 73 | 74 | means either the GNU General Public License, Version 2.0, the GNU Lesser 75 | General Public License, Version 2.1, the GNU Affero General Public 76 | License, Version 3.0, or any later versions of those licenses. 77 | 78 | 1.13. "Source Code Form" 79 | 80 | means the form of the work preferred for making modifications. 81 | 82 | 1.14. "You" (or "Your") 83 | 84 | means an individual or a legal entity exercising rights under this 85 | License. For legal entities, "You" includes any entity that controls, is 86 | controlled by, or is under common control with You. For purposes of this 87 | definition, "control" means (a) the power, direct or indirect, to cause 88 | the direction or management of such entity, whether by contract or 89 | otherwise, or (b) ownership of more than fifty percent (50%) of the 90 | outstanding shares or beneficial ownership of such entity. 91 | 92 | 93 | 2. License Grants and Conditions 94 | 95 | 2.1. Grants 96 | 97 | Each Contributor hereby grants You a world-wide, royalty-free, 98 | non-exclusive license: 99 | 100 | a. under intellectual property rights (other than patent or trademark) 101 | Licensable by such Contributor to use, reproduce, make available, 102 | modify, display, perform, distribute, and otherwise exploit its 103 | Contributions, either on an unmodified basis, with Modifications, or 104 | as part of a Larger Work; and 105 | 106 | b. under Patent Claims of such Contributor to make, use, sell, offer for 107 | sale, have made, import, and otherwise transfer either its 108 | Contributions or its Contributor Version. 109 | 110 | 2.2. Effective Date 111 | 112 | The licenses granted in Section 2.1 with respect to any Contribution 113 | become effective for each Contribution on the date the Contributor first 114 | distributes such Contribution. 115 | 116 | 2.3. Limitations on Grant Scope 117 | 118 | The licenses granted in this Section 2 are the only rights granted under 119 | this License. No additional rights or licenses will be implied from the 120 | distribution or licensing of Covered Software under this License. 121 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 122 | Contributor: 123 | 124 | a. for any code that a Contributor has removed from Covered Software; or 125 | 126 | b. for infringements caused by: (i) Your and any other third party's 127 | modifications of Covered Software, or (ii) the combination of its 128 | Contributions with other software (except as part of its Contributor 129 | Version); or 130 | 131 | c. under Patent Claims infringed by Covered Software in the absence of 132 | its Contributions. 133 | 134 | This License does not grant any rights in the trademarks, service marks, 135 | or logos of any Contributor (except as may be necessary to comply with 136 | the notice requirements in Section 3.4). 137 | 138 | 2.4. Subsequent Licenses 139 | 140 | No Contributor makes additional grants as a result of Your choice to 141 | distribute the Covered Software under a subsequent version of this 142 | License (see Section 10.2) or under the terms of a Secondary License (if 143 | permitted under the terms of Section 3.3). 144 | 145 | 2.5. Representation 146 | 147 | Each Contributor represents that the Contributor believes its 148 | Contributions are its original creation(s) or it has sufficient rights to 149 | grant the rights to its Contributions conveyed by this License. 150 | 151 | 2.6. Fair Use 152 | 153 | This License is not intended to limit any rights You have under 154 | applicable copyright doctrines of fair use, fair dealing, or other 155 | equivalents. 156 | 157 | 2.7. Conditions 158 | 159 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in 160 | Section 2.1. 161 | 162 | 163 | 3. Responsibilities 164 | 165 | 3.1. Distribution of Source Form 166 | 167 | All distribution of Covered Software in Source Code Form, including any 168 | Modifications that You create or to which You contribute, must be under 169 | the terms of this License. You must inform recipients that the Source 170 | Code Form of the Covered Software is governed by the terms of this 171 | License, and how they can obtain a copy of this License. You may not 172 | attempt to alter or restrict the recipients' rights in the Source Code 173 | Form. 174 | 175 | 3.2. Distribution of Executable Form 176 | 177 | If You distribute Covered Software in Executable Form then: 178 | 179 | a. such Covered Software must also be made available in Source Code Form, 180 | as described in Section 3.1, and You must inform recipients of the 181 | Executable Form how they can obtain a copy of such Source Code Form by 182 | reasonable means in a timely manner, at a charge no more than the cost 183 | of distribution to the recipient; and 184 | 185 | b. You may distribute such Executable Form under the terms of this 186 | License, or sublicense it under different terms, provided that the 187 | license for the Executable Form does not attempt to limit or alter the 188 | recipients' rights in the Source Code Form under this License. 189 | 190 | 3.3. Distribution of a Larger Work 191 | 192 | You may create and distribute a Larger Work under terms of Your choice, 193 | provided that You also comply with the requirements of this License for 194 | the Covered Software. If the Larger Work is a combination of Covered 195 | Software with a work governed by one or more Secondary Licenses, and the 196 | Covered Software is not Incompatible With Secondary Licenses, this 197 | License permits You to additionally distribute such Covered Software 198 | under the terms of such Secondary License(s), so that the recipient of 199 | the Larger Work may, at their option, further distribute the Covered 200 | Software under the terms of either this License or such Secondary 201 | License(s). 202 | 203 | 3.4. Notices 204 | 205 | You may not remove or alter the substance of any license notices 206 | (including copyright notices, patent notices, disclaimers of warranty, or 207 | limitations of liability) contained within the Source Code Form of the 208 | Covered Software, except that You may alter any license notices to the 209 | extent required to remedy known factual inaccuracies. 210 | 211 | 3.5. Application of Additional Terms 212 | 213 | You may choose to offer, and to charge a fee for, warranty, support, 214 | indemnity or liability obligations to one or more recipients of Covered 215 | Software. However, You may do so only on Your own behalf, and not on 216 | behalf of any Contributor. You must make it absolutely clear that any 217 | such warranty, support, indemnity, or liability obligation is offered by 218 | You alone, and You hereby agree to indemnify every Contributor for any 219 | liability incurred by such Contributor as a result of warranty, support, 220 | indemnity or liability terms You offer. You may include additional 221 | disclaimers of warranty and limitations of liability specific to any 222 | jurisdiction. 223 | 224 | 4. Inability to Comply Due to Statute or Regulation 225 | 226 | If it is impossible for You to comply with any of the terms of this License 227 | with respect to some or all of the Covered Software due to statute, 228 | judicial order, or regulation then You must: (a) comply with the terms of 229 | this License to the maximum extent possible; and (b) describe the 230 | limitations and the code they affect. Such description must be placed in a 231 | text file included with all distributions of the Covered Software under 232 | this License. Except to the extent prohibited by statute or regulation, 233 | such description must be sufficiently detailed for a recipient of ordinary 234 | skill to be able to understand it. 235 | 236 | 5. Termination 237 | 238 | 5.1. The rights granted under this License will terminate automatically if You 239 | fail to comply with any of its terms. However, if You become compliant, 240 | then the rights granted under this License from a particular Contributor 241 | are reinstated (a) provisionally, unless and until such Contributor 242 | explicitly and finally terminates Your grants, and (b) on an ongoing 243 | basis, if such Contributor fails to notify You of the non-compliance by 244 | some reasonable means prior to 60 days after You have come back into 245 | compliance. Moreover, Your grants from a particular Contributor are 246 | reinstated on an ongoing basis if such Contributor notifies You of the 247 | non-compliance by some reasonable means, this is the first time You have 248 | received notice of non-compliance with this License from such 249 | Contributor, and You become compliant prior to 30 days after Your receipt 250 | of the notice. 251 | 252 | 5.2. If You initiate litigation against any entity by asserting a patent 253 | infringement claim (excluding declaratory judgment actions, 254 | counter-claims, and cross-claims) alleging that a Contributor Version 255 | directly or indirectly infringes any patent, then the rights granted to 256 | You by any and all Contributors for the Covered Software under Section 257 | 2.1 of this License shall terminate. 258 | 259 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user 260 | license agreements (excluding distributors and resellers) which have been 261 | validly granted by You or Your distributors under this License prior to 262 | termination shall survive termination. 263 | 264 | 6. Disclaimer of Warranty 265 | 266 | Covered Software is provided under this License on an "as is" basis, 267 | without warranty of any kind, either expressed, implied, or statutory, 268 | including, without limitation, warranties that the Covered Software is free 269 | of defects, merchantable, fit for a particular purpose or non-infringing. 270 | The entire risk as to the quality and performance of the Covered Software 271 | is with You. Should any Covered Software prove defective in any respect, 272 | You (not any Contributor) assume the cost of any necessary servicing, 273 | repair, or correction. This disclaimer of warranty constitutes an essential 274 | part of this License. No use of any Covered Software is authorized under 275 | this License except under this disclaimer. 276 | 277 | 7. Limitation of Liability 278 | 279 | Under no circumstances and under no legal theory, whether tort (including 280 | negligence), contract, or otherwise, shall any Contributor, or anyone who 281 | distributes Covered Software as permitted above, be liable to You for any 282 | direct, indirect, special, incidental, or consequential damages of any 283 | character including, without limitation, damages for lost profits, loss of 284 | goodwill, work stoppage, computer failure or malfunction, or any and all 285 | other commercial damages or losses, even if such party shall have been 286 | informed of the possibility of such damages. This limitation of liability 287 | shall not apply to liability for death or personal injury resulting from 288 | such party's negligence to the extent applicable law prohibits such 289 | limitation. Some jurisdictions do not allow the exclusion or limitation of 290 | incidental or consequential damages, so this exclusion and limitation may 291 | not apply to You. 292 | 293 | 8. Litigation 294 | 295 | Any litigation relating to this License may be brought only in the courts 296 | of a jurisdiction where the defendant maintains its principal place of 297 | business and such litigation shall be governed by laws of that 298 | jurisdiction, without reference to its conflict-of-law provisions. Nothing 299 | in this Section shall prevent a party's ability to bring cross-claims or 300 | counter-claims. 301 | 302 | 9. Miscellaneous 303 | 304 | This License represents the complete agreement concerning the subject 305 | matter hereof. If any provision of this License is held to be 306 | unenforceable, such provision shall be reformed only to the extent 307 | necessary to make it enforceable. Any law or regulation which provides that 308 | the language of a contract shall be construed against the drafter shall not 309 | be used to construe this License against a Contributor. 310 | 311 | 312 | 10. Versions of the License 313 | 314 | 10.1. New Versions 315 | 316 | Mozilla Foundation is the license steward. Except as provided in Section 317 | 10.3, no one other than the license steward has the right to modify or 318 | publish new versions of this License. Each version will be given a 319 | distinguishing version number. 320 | 321 | 10.2. Effect of New Versions 322 | 323 | You may distribute the Covered Software under the terms of the version 324 | of the License under which You originally received the Covered Software, 325 | or under the terms of any subsequent version published by the license 326 | steward. 327 | 328 | 10.3. Modified Versions 329 | 330 | If you create software not governed by this License, and you want to 331 | create a new license for such software, you may create and use a 332 | modified version of this License if you rename the license and remove 333 | any references to the name of the license steward (except to note that 334 | such modified license differs from this License). 335 | 336 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 337 | Licenses If You choose to distribute Source Code Form that is 338 | Incompatible With Secondary Licenses under the terms of this version of 339 | the License, the notice described in Exhibit B of this License must be 340 | attached. 341 | 342 | Exhibit A - Source Code Form License Notice 343 | 344 | This Source Code Form is subject to the 345 | terms of the Mozilla Public License, v. 346 | 2.0. If a copy of the MPL was not 347 | distributed with this file, You can 348 | obtain one at 349 | http://mozilla.org/MPL/2.0/. 350 | 351 | If it is not possible or desirable to put the notice in a particular file, 352 | then You may include the notice in a location (such as a LICENSE file in a 353 | relevant directory) where a recipient would be likely to look for such a 354 | notice. 355 | 356 | You may add additional accurate notices of copyright ownership. 357 | 358 | Exhibit B - "Incompatible With Secondary Licenses" Notice 359 | 360 | This Source Code Form is "Incompatible 361 | With Secondary Licenses", as defined by 362 | the Mozilla Public License, v. 2.0. 363 | 364 | -------------------------------------------------------------------------------- /MACHINES.md: -------------------------------------------------------------------------------- 1 | # Machine data 2 | 3 | The purpose of this file is to record information about specific hardware, to support 4 | the design process. It will be good to make references to authoritative material 5 | when possible. 6 | 7 | NOTE: There is much hardware information in the older documents in the historical/ directory, 8 | I will move it into this file as soon as I can. 9 | 10 | ## Interleaving atomic and non-atomic data 11 | 12 | (From Issue #10) The question is whether it is well-defined to 13 | interleave atomic and non-atomic data on platforms that do not have byte and 14 | half-word atomics and that may wish to implement those using RMW on word atomics. This 15 | comes down to the interaction of normal stores with exclusive stores. 16 | 17 | - On ARM, a normal store clears any reservation it interacts with, and the reservation 18 | size is at least 8 bytes (ARMv7 manual). Thus a RMW byte atomic that interacts with 19 | an adjacent non-atomic byte will be fine. (Only an issue on very early ARMv6 and 20 | earlier architectures.) 21 | - On MIPS, where we have word (and on MIPS64, doubleword) exclusives, the manual states 22 | that a normal store to a reserved address clears the reservation. 23 | - On Power8, the reservation size is 16 bytes and the manual states that a normal store 24 | to a reserved address clears the reservation. 25 | 26 | In those cases a store to a byte next to an atomic byte can happen before the load-exclusive 27 | or after the store-exclusive, and if it happens between the load-exclusive and the 28 | store-exclusive then it will force a retry. 29 | 30 | # General guidance 31 | 32 | (NOTE: The following has been copied from the original proposal document and cleaned up only superficially. Some of the information below is known to be incomplete and/or naive, it is annotated when I know there's a problem.) 33 | 34 | Not all hardware supports atomic operations at every granularity we need. ARMv6 and MIPS32 do not provide 8-byte atomic operations, for example, and implementing the atomic operations on Float64Array on those platforms requires the use of other techniques. Usually it means resorting to an external lock (hidden within the implementation of the atomic operations). 35 | 36 | (NOTE: There are no longer atomics on Float32Array or Float64Array.) 37 | 38 | As far as an external lock is concerned, there are several approaches: 39 | 40 | * A global lock (though not a global lock per type) 41 | * A lock per SharedArrayBuffer (a crude sharded lock) 42 | * A lock per address range (a more sophisticated sharded lock) 43 | 44 | In the implementation, any atomic access from C++ (from an interpreter primitive, say) must use the same mechanism as an atomic access from jitted code. This is true for atomic accesses at all sizes but tends to work out because both the C++ compiler and the JIT use the obvious atomic instructions; for 64-bit accesses on platforms with no 64-bit atomics additional coordination is required. 45 | 46 | If the hardware supports atomic operations at a granularity greater than what we need for a particular access then the atomic operation on the smaller size can be implemented using a read-modify-write sequence with the wider atomic operation. 47 | 48 | (NOTE: This is only correct because of observations made in the earlier section, Interleaving atomic and non-atomic data.) 49 | 50 | Implementing compareExchange on a LL/SC architecture is not straightforward. The primitive may only fail if the value in the cell is different from the expected value. However, in the case of LL/SC, the update will fail if the cell was written even with the old value. Thus the implementation of compareExchange must retry the update if the update failed but the cell contains the old value. 51 | 52 | (NOTE: I suppose in that case, the compareExchange could be seen as having succeeded.) 53 | 54 | A full barrier is not always needed before and/or after an operation that contains "atomically do". Notably there is useful advice in the JSR133 cookbook [Cookbook] and on the Cambridge RelaxedMemory Concurrency Group's page about C++ atomics [C++11 mappings]. For example, the load in an Atomics.load() will need no barriers before it but LoadLoad and LoadStore barriers after it. 55 | 56 | ## asm.js mapping 57 | 58 | Weakly ordered C++ atomic accesses can be mapped to the strongly ordered accesses defined above, at an occasional loss in performance. 59 | 60 | Volatile C++ accesses, which are (arguably incorrectly) used for inter-thread communication in some x86 programs, must generally be mapped to Atomics.load and Atomics.store: JITs will usually hoist or common normal loads and stores. The mapping of volatiles to sequentially consistent atomics also hides the difference between weakly and strongly ordered hardware, which is possibly desirable in programs for the web. (If we later add support for relaxed atomics they may be used to implement volatile at lower cost.) A consequence of this is that volatiles are forced to be aligned, which they are not in C++, but this seems reasonable. PNaCl makes the same choice [PNaCl volatile]. 61 | 62 | C++ atomics on integral types (integer and char types) are defined to be initializable in the same way as the integral types, eg, with zero [C++11 atomics]. Our atomics can be used to implement C++ atomics for up to int32 size that have the same size as the underlying integral, which is useful (existing code may assume this). For other data types the translator can use techniques in the "General guidance" section. 63 | 64 | C++ has additional templates, constants, and methods that are used to determine whether an atomic access will be implemented by a lock or by a lock-free primitive. Emscripten must expose this appropriately in its headers and must make up for the rest through code generation. 65 | 66 | Some extensions to the asm.js spec are required to support shared views and inlined Atomics operations, these are in a companion document [asm.js additions]. 67 | 68 | ## x86 and x86_64 69 | 70 | Discussion based on the Intel manual set [Intel]. 71 | 72 | ### Single-copy atomicity 73 | 74 | The architecture is single-copy atomic in a large number of cases (volume 3A section 8.1.1): since the i486, for naturally aligned data up to four bytes; since the Pentium, for naturally aligned data up to eight bytes; since the P6, also for up to eight unaligned bytes as long as they are all within the same cache line; and since the Core 2 Duo, for up to eight unaligned bytes in general. Note that SSE data, being 16 bytes, are not read or written atomically (to or from SSE registers) even when properly aligned. 75 | 76 | ### Memory consistency model 77 | 78 | The memory model is not quite sequentially consistent; store-to-load consistency, where a load following a store must wait for the store to complete, must be implemented by the program with an explicit fence. (If the load is to the word being stored then the load will read the stored value without the fence). The model is known as TSO, total store order. There is a good paper by the Cambridge Relaxed Memory Concurrency Group describing the memory model [Cam cacm]. 79 | 80 | The fence is an MFENCE instruction on SSE2-equipped systems and later. On earlier systems it is a locked memory no-op; 81 | Linux uses "LOCK; ADD [esp], 0". 82 | 83 | ### Synchronization 84 | 85 | Synchronization is via LOCK-prefixed instructions such as CMPXCHG, ADD, and XADD, or via instructions where the lock is implicit, such as XCHG. These can be applied in various ways to reduce register pressure, get rid of loops, etc. On 32bit 86 | systems these instructions work on data up to four bytes; on 64bit systems, on data up to eight bytes. Only integer registers can be targeted. CMPXCHG8B / CMPXCHG16B can be used to implement 8-byte compare-exchange on 32-bit systems and 16-byte compare-exchange on 64-bit systems. 87 | 88 | The x86 family has a PAUSE instruction that improves the performance of spinlocks. 89 | 90 | ## ARM32 and ARM64 91 | 92 | The discussion is based on the ARMv6M, ARMv7A, and ARMv8A architecture reference manuals [ARM]. 93 | 94 | ### Single-copy atomicity 95 | 96 | The ARMv6 manual states that 1, 2, and 4 byte aligned accesses are single-copy atomic. A few multi-word accesses are executed as sequences of single-copy atomic operations. 97 | 98 | The ARMv7 manual states that 1, 2, and 4 byte aligned accesses are single-copy atomic, that LDREXD and STREXD (8 byte) aligned accesses are singlecopy atomic, and aligned LDRD and STRD are single-copy atomic when the processor has support for LPAE (standard with Cortex-A15 and later). A number of multi-word accesses are executed as sequences of single-copy atomic accesses. 99 | 100 | The ARMv7 manual also says that when two stores of different size are racing to the same location and they would otherwise be single-copy atomic, then bets are off about atomicity. 101 | 102 | The ARMv8 manual states that 8 byte aligned accesses (in 64bit mode) are single-copy atomic, and that LDREXD and STREXD (16 byte) aligned accesses are single-copy atomic if the store is used with the load and the store succeeds. The ARMv8 manual also states that single-copy atomicity is guaranteed only for instructions that target general-purpose registers, ie, is not guaranteed for direct floating-point loads and stores or for SIMD data. More discussion at [JMM atomicity]. 103 | 104 | ### Memory consistency model 105 | 106 | For practical purposes the architecture is weakly ordered; it is possible for some memory to be designated as "strongly ordered" but my understanding is that "normal" memory is weakly ordered. 107 | 108 | There are two synchronization instructions, "DSB" which is a completion barrier (store has reached the coherent memory system) and "DMB" which is an ordering barrier (orders the write buffer). Each can take an "option"; the most interesting option is "ST", which so far as I understand can be used to create a cheaper StoreStore barrier. Only DMB is required for normal application software. 109 | 110 | (NOTE: The story is more complicated than that. DMB has some kind of transitivity built into it, in order to make memory properly coherent.) 111 | 112 | ARMv6 makes the DSB and DMB operations available through coprocessor instructions [ARM barrier]. 113 | 114 | ### Synchronization 115 | 116 | Synchronization is via a family of load-exclusive/store-exclusive instructions (LDREX and STREX), for sizes of 1, 2, 4, and (since ARMv6K and on all ARMv7 and ARMv8 systems) 8 bytes. These do not imply any ordering of other memory operations. ARMv8 additionally introduces a class of load-acquire and store-release instructions (LDAEX and STLEX), for sizes of 1, 2, 4, and 8 bytes. These imply memory ordering as well as exclusivity, and reduce the need to use DMB. 117 | 118 | ## POWER and PowerPC 119 | 120 | Discussion based on the POWER5 / PowerPC 2.02 book set [PowerPC], which is quite dated. It appears to cover both 32bit and 64bit models. 121 | 122 | ### Single-copy atomicity 123 | 124 | The architecture is single-copy atomic for naturally aligned accesses of 1, 2, 4, and 8 bytes (vol 2 section 1.4). 125 | 126 | ### Memory consistency model 127 | 128 | Memory is weakly consistent, like ARM. (In the literature, POWER appears to be considered the weakest of the mainstream architectures.) The architecture has several barrier instructions. The most comprehensive, sync, orders all instructions preceding the barrier before any memory accesses succeeding the barrier. A cheaper instruction, lwsync, creates LoadLoad, LoadStore, and StoreStore barriers. The I/O barrier, eieio, can be used to create a StoreStore barrier by itself. 129 | 130 | (NOTE: As for ARM, some transitivity is built into the sync instruction.) 131 | 132 | ### Synchronization 133 | 134 | There are load-linked/store-conditional instructions (here called "load word and reserve" and "store word conditional") for 4-byte and 8-byte aligned data. Clearly the 4-byte version can be used to implement 8-bit and 16-bit operations using read-modify-write; the LL/SC reservation will work just fine for that. 135 | 136 | A particular form of the NOP instruction can be used to effect a pause for the purpose of implementing spinlocks [PPC Pause]. 137 | 138 | ## MIPS32 139 | 140 | Discussion based on the MIPS32 Rev 5.03 manual set [MIPS32]. For practical purposes we can probably stick to Release 2 or later; Release 2 came in 2002, Release 3 in 2010. 141 | 142 | ### Single-copy atomicity 143 | 144 | "Architecture rule B2", vol IIA sec B.4.1, suggests strongly that the memory model is at least single-copy atomic for naturally aligned data, because that section states that misaligned accesses are "not guaranteed to be atomic as observed from other threads, processors, and I/O devices". It seems likely (based on prose I believe I saw elsewhere but cannot find again) that the memory model is single-copy atomic up to 64 bits, ie, floating point loads and stores are single-copy 145 | atomic. 146 | 147 | ### Memory consistency model 148 | 149 | The consistency model of MIPS32 appears to be implementation defined, and for practical purposes we should treat MIPS as weakly ordered and similar to ARM and Power. The following is based on the "Programming notes" for the SYNC instruction. 150 | 151 | Evidently some implementations are strongly consistent, but that's not required, and the SYNC instruction performs various kinds of memory barriers (that are noops on SC systems). 152 | 153 | The SYNC instruction can be used as-is to perform all barriers, or it can be configured with flags to only provide certain kinds of barriers (LoadLoad, LoadStore, etc, and sometimes a combination). Without an option the SYNC is a "completion barrier", it ensures that a write has actually gone out to the memory system. With an option the SYNC is an "ordering barrier", it ensures that reads and/or writes before the SYNC will touch the memory system before reads and/or writes after the SYNC. 154 | 155 | ### Synchronization 156 | 157 | MIPS32 has a 32-bit LL/SC pair. Clearly this can be used to implement 8-bit and 16-bit operations using read-modify-write; the LL/SC reservation will work just fine for that. 158 | 159 | (NOTE: That is only true because of constraints in "Interleaving atomic and non-atomic data", above.) 160 | 161 | In Release 2.5 a PAUSE instruction was introduced that improves the performance of spinlocks. This release also introduced new options for the SYNC instruction. (Volume 1, section 2.1.2.2.) 162 | 163 | # References 164 | 165 | [ARM] ARM Architecture Reference Manual. Downloadable from http://infocenter.arm.com, free registration required. 166 | 167 | [ARM barrier] Leif Lindholm, "Memory access ordering part 3 memory access ordering in the ARM Architecture" 168 | http://community.arm.com/groups/processors/blog/2011/10/19/memoryaccessorderingpart3memoryaccessorderinginthearmarchitecture 169 | ; also comments on this draft by Lindholm. 170 | 171 | [asm.js additions] asm.js: Semantic additions for shared memory and atomics: http://tc39.github.io/ecmascript_sharedmem/asmjs_shmem.html. 172 | 173 | [C++11 atomics] C++11 standard, section [atomics.types.generic], paragraphs 4 and 5. 174 | 175 | [C++11 mappings] http://www.cl.cam.ac.uk/~pes20/cpp/cpp0xmappings.html. 176 | 177 | [Cam cacm] http://www.cl.cam.ac.uk/~pes20/weakmemory/cacm.pdf. 178 | 179 | [Cookbook] JSR133 Cookbook for Compiler Writers. http://g.oswego.edu/dl/jmm/cookbook.html. 180 | 181 | [Intel] "Intel® 64 and IA32 Architectures Software Developer’s Manual Volumes 1, 2A, 2B, and 3A: System Programming Guide, Part 1", March 2012. 182 | 183 | [JMM atomicity] http://mail.openjdk.java.net/pipermail/jmmdev/2014February/000011.html with subsequent and following messages in that thread. 184 | 185 | [MIPS32] MIPS Architecture For Programmers Volume IA, IIA, III, Revision 5.03, Sept 9, 2013. http://www.imgtec.com/mips/architectures/mips32.asp , free registration required. 186 | 187 | [PNaCl volatile] https://developer.chrome.com/nativeclient/reference/pnaclccpplanguagesupport#volatilememoryaccesses. 188 | 189 | [PowerPC] "PowerPC User Instruction Set Architecture" and "PowerPC Virtual Environment Architecture", version 2.02, January 2005. These describe the POWER5 architecture which coincided with PPC at that time. Downloaded free of charge from IBM: http://www.ibm.com/developerworks/systems/library/esarchguidev2.html. 190 | 191 | [PPC pause] http://stackoverflow.com/questions/5425506/equivalentofx86pauseinstructionforppc. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ecmascript_sharedmem 2 | 3 | This is a specification for Shared Memory and Atomics for ECMAScript, a proposal submitted to Ecma TC39 and currently at Stage 4 in the ECMAScript approval process. 4 | 5 | **IMPORTANT NOTE:** As of February 2017 this proposal has been merged into the [ECMAScript specification](https://tc39.github.io/ecma262/). Bug fixing and further evolution take place in that document, and the prose in the present repository will increasingly become irrelevant. If you find bugs, please check the ECMAScript specification first, and if the bugs are also in that version then please file bugs [in the ecma262 bug tracker](https://github.com/tc39/ecma262/issues), not in this repository. 6 | 7 | ## Documentation and other materials 8 | 9 | * [Formatted specification](http://tc39.github.io/ecmascript_sharedmem/shmem.html) 10 | * [DOM companion specification](http://tc39.github.io/ecmascript_sharedmem/dom_shmem.html) 11 | * [asm.js companion specification](http://tc39.github.io/ecmascript_sharedmem/asmjs_shmem.html) 12 | * [Simple tutorial introduction](TUTORIAL.md) 13 | * [Demo programs and other examples](DEMOS.md) 14 | * [Frequently asked questions](FAQ.md) 15 | * [High-level design issues, cross-cutting concerns, security concerns, etc](DISCUSSION.md) 16 | * Slide decks for presentations given to Ecma TC39: 17 | * [September 2015](https://github.com/tc39/ecmascript_sharedmem/blob/master/tc39/presentation-sept-2015.odp) 18 | * [January 2016](https://github.com/tc39/ecmascript_sharedmem/blob/master/tc39/presentation-jan-2016.odp) 19 | 20 | ## Implementations 21 | 22 | Firefox, Chrome and WebKit ship with prototype implementations of the proposal; these are largely compatible. 23 | 24 | * The feature is enabled by default in Firefox Nightly; starting with Firefox 46, users of Developer Edition, Aurora, Beta, and Release can visit `about:config` and set the option `javascript.options.shared_memory` to `true`. 25 | * The feature is off by default in Chrome, but can be enabled by passing the command line options `--js-flags=--harmony-sharedarraybuffer` and `--enable-blink-feature=SharedArrayBuffer`. (Known to work in Chrome 48.) 26 | * The feature is enabled by default in WebKit Nightly and Safari Technology Preview as of STP 20. 27 | 28 | ## Miscellaneous 29 | 30 | The sources for the specs are in the tc39/ subdirectory and the formatted versions are generated with the `format.sh` script. 31 | 32 | -------------------------------------------------------------------------------- /TUTORIAL.md: -------------------------------------------------------------------------------- 1 | # Shared memory - a brief tutorial 2 | 3 | ## Concurrent agents 4 | 5 | In a browser setting it's natural to use WebWorkers for shared agents. To recap, workers are created by allocating ```Worker``` objects, passing a URL of the script to be run in the worker: 6 | ```js 7 | var w = new Worker("myworker.js") 8 | ``` 9 | The worker and its creator communicate over a message channel. In the creator, a call to ```w.postMessage``` sends a message to the worker, and a handler on the worker object receives messages as events: 10 | ```js 11 | w.postMessage("hi"); // send "hi" to the worker 12 | w.onmessage = function (ev) { 13 | console.log(ev.data); // prints "ho" 14 | } 15 | ``` 16 | Meanwhile, in the worker, a global handler receives messages as events, and a call to the global ```postMessage``` function sends a message back to the worker's creator: 17 | ```js 18 | onmessage = function (ev) { 19 | console.log(ev.data); // prints "hi" 20 | postMessage("ho"); // sends "ho" back to the creator 21 | } 22 | ``` 23 | Many types of data - not just strings - can be sent across the channel and will arrive with the correct type and structure at the destination. 24 | 25 | ## Allocating shared memory, and sharing it 26 | 27 | To allocate shared memory, just allocate a SharedArrayBuffer: 28 | 29 | ```js 30 | var sab = new SharedArrayBuffer(1024); // 1KiB shared memory 31 | ``` 32 | 33 | The creator can share this memory with the worker by sending it to the worker using the standard ```postMessage``` method: 34 | ```js 35 | w.postMessage(sab) 36 | ``` 37 | In the worker, this object is received as the data property of the event: 38 | ```js 39 | var sab; 40 | onmessage = function (ev) { 41 | sab = ev.data; // 1KiB shared memory, the same memory as in the parent 42 | } 43 | ``` 44 | 45 | Memory can be created in any agent and then shared with any other agent, and can be shared among many agents simultaneously. 46 | 47 | ## Creating views on shared memory 48 | 49 | A SharedArrayBuffer is like an ArrayBuffer, apart from its memory being shared, and the memory is accessed in the same way as an ArrayBuffer's memory is: by creating views on the buffer, and then accessing the memory via the view using standard array access syntax. The same view types can be applied to SharedArrayBuffer as to ArrayBuffer, from Int8Array to Float64Array. 50 | 51 | Suppose the creator of the shared memory wants to share a large array of prime numbers with its workers, without any intent of modifying that array. It allocates the memory, fills it, and sends it: 52 | 53 | ```js 54 | var sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000); // 100000 primes 55 | var ia = new Int32Array(sab); // ia.length == 100000 56 | var primes = new PrimeGenerator(); 57 | for ( let i=0 ; i < ia.length ; i++ ) 58 | ia[i] = primes.next(); 59 | w.postMessage(ia); 60 | ``` 61 | 62 | The worker receives an Int32Array because that's what was sent: 63 | 64 | ```js 65 | var ia; 66 | onmessage = function (ev) { 67 | ia = ev.data; // ia.length == 100000 68 | console.log(ia[37]); // prints 163, the 38th prime 69 | } 70 | ``` 71 | 72 | ## Updating shared memory, and visibility 73 | 74 | Since the memory is truly shared, a write in one agent will be observed in all the other agents that share that memory: 75 | 76 | ```js 77 | console.log(ia[37]); // Prints 163 78 | ia[37] = 123456; 79 | ``` 80 | 81 | A while after the assignment happened in the writer the change will show up in another agent: 82 | 83 | ```js 84 | console.log(ia[37]); // Prints 123456, maybe, eventually 85 | ``` 86 | 87 | One of the trickiest aspects of shared memory is that it's hard to know how long it will take for a write to propagate from one agent to another. How quickly that happens is actually system-dependent, for a very broad definition of system including the CPU, the operating system, the browser, and in fact whatever else is happening on the system at the time of the write and read. 88 | 89 | In fact, without additional *synchronization* the above program is not well-defined. (It won't crash, but the agent that does the reading may observe other values besides 163 and 123456 - this is counter-intuitive, but it can happen.) Synchronization is implemented using atomic operations, described next. 90 | 91 | ## Atomic operations 92 | 93 | A new global object called ```Atomics``` provides a number of new *atomic operations* as static methods. Atomic operations serve several related purposes. 94 | 95 | ### Predictable values are written and read 96 | 97 | An atomic read from an array element that was written with an atomic write will only observe the value that was in the cell before the write, or the value that was written. 98 | 99 | In the example above, if the writer uses an atomic write: 100 | 101 | ```js 102 | console.log(ia[37]); // Prints 163, the 38th prime 103 | Atomics.store(ia, 37, 123456); 104 | ``` 105 | 106 | then the reader can use an atomic read in a loop to wait for the value to change, and once it does change it will change predictably: 107 | 108 | ```js 109 | while (Atomics.load(ia, 37) == 163) 110 | ; 111 | console.log(ia[37]); // Prints 123456 112 | ``` 113 | 114 | ### Atomic operations enforce ordering 115 | 116 | When an atomic read observes a value there is a guarantee that all the other writes (atomic or not) that the writer performed before it performed the write that was observed, will also be observable. 117 | 118 | That that might not be true is counter-intuitive, but it can happen. Suppose the writer does this: 119 | ```js 120 | ia[42] = 314159; // was 191 121 | ia[37] = 123456; // was 163 122 | ``` 123 | Suppose another worker reads those two values: 124 | ```js 125 | console.log(ia[37]); 126 | console.log(ia[42]); 127 | ``` 128 | The reader may print 123456 and 191, even though it would seem that that should not happen. The reason is that the writes may be reordered by the compiler and (more often) the CPU. 129 | 130 | Atomic operations create points of ordering in the program. If ia[37] is written atomically, all writes performed before the atomic write will be observable no later than the write to ia[37] is observed: 131 | ```js 132 | ia[42] = 314159; // was 191 133 | Atomics.store(ia, 37, 123456); // was 163 134 | ``` 135 | 136 | ```js 137 | while (Atomics.load(ia, 37) == 163) 138 | ; 139 | console.log(ia[37]); // Will print 123456 140 | console.log(ia[42]); // Will print 314159 141 | ``` 142 | 143 | (Similarly, it is possible for reads to be performed out of order. Atomic operations also order the reads.) 144 | 145 | ### Atomic operations are not interruptible 146 | 147 | Atomic read-modify-write operations guarantee that no other write gets to happen until the modified value is written back. 148 | 149 | Suppose there is a counter in shared memory, at ia[112], that both agents need to increment. If they increment it with the obvious ```ia[112]++``` then there is a risk that they will both do so at the same time, and an update can be lost (and the result value can be garbage). The reason is that in order to perform the increment the CPU must load the value, add one to it, and store it back. In the mean time, some other CPU may have done the same thing. 150 | 151 | Instead, using ```Atomics.add(ia, 112, 1)``` will guarantee that that race does not happen: each CPU gets to load, add, and store in isolation without being interrupted, and the counter will be updated correctly. 152 | 153 | ### Run-down of atomic operations 154 | 155 | The following operations are available when ```array``` is an Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array or Uint32Array: 156 | 157 | Operation | Function 158 | ----------------------------------------------|---------- 159 | load(array, index) | return the value of array[index] 160 | store(array, index, value) | store value in array[index], return value 161 | compareExchange(array, index, oldval, newval) | if array[index] == oldval then store newval in array[index], return the old value of array[index] 162 | exchange(array, index, value) | store value in array[index], return the old value of array[index] 163 | add(array, index, value) | add value to array[index], return the old value of array[index] 164 | sub(array, index, value) | subtract value from array[index], return the old value of array[index] 165 | and(array, index, value) | bitwise-and value into array[index], return the old value of array[index] 166 | or(array, index, value) | bitwise-or value into array[index], return the old value of array[index] 167 | xor(array, index, value) | bitwise-xor value into array[index], return the old value of array[index] 168 | 169 | ## Waiting for things to happen 170 | 171 | In the examples above one agent used a loop to wait for a value to be changed by the other agent; the change is a signal that the agent can stop waiting and can go ahead with whatever it needs to do next. 172 | 173 | Such *spin loops* are a poor use of computer time unless the wait is very brief; instead, a waiting agent can put itself to sleep, and can instead be woken up explicitly by the other agent. The Atomics object provides a primitive mechanism for this in the two methods `wait` and `wake`. 174 | 175 | In our example, the writing agent now does this: 176 | 177 | ```js 178 | console.log(ia[37]); // Prints 163 179 | Atomics.store(ia, 37, 123456); 180 | Atomics.wake(ia, 37, 1); 181 | ``` 182 | 183 | and the reading agent does this: 184 | 185 | ```js 186 | Atomics.wait(ia, 37, 163); 187 | console.log(ia[37]); // Prints 123456 188 | ``` 189 | 190 | The way this works is that once it has performed a write, the writer requests to wake up one agent that's sleeping on location ia[37]. Meanwhile, the reader requests to go to sleep, provided the value of ia[37] is still 163. Thus if the writer has already performed its write the reader will just continue on, and otherwise it will sleep, waiting to be woken by the writer. 191 | 192 | ## Abstractions 193 | 194 | In practice some of the shared memory facilities, especially `wait` and `wake`, can be hard to use correctly and efficiently. It is therefore useful to build abstractions (in JavaScript) around these to simplify their use. For example, among the demos you will find traditional mutexes and condition variables; barriers; and so-called "synchronic" objects that are containers for simple atomic values and which have a built-in (and efficient) wait-for-update functionality. 195 | 196 | ## Subtleties and practical advice 197 | 198 | (Additional topics: race conditions; deadlocks; flatjs?; Web APIs and shared memory.) 199 | 200 | ### Blocking on the browser's main thread 201 | 202 | There is nothing inherently problematic about waiting in the browser's main thread - a `wait` has the same meaning as a loop that waits for a wakeup flag to be set. However, unless there's a guarantee that the wait will be brief it's often better to avoid such a wait, and instead have a "master worker" that acts on the main thread's behalf and communicates with the main thread using message passing. The "master worker" can wait indefinitely without impacting the responsiveness of the browser. 203 | 204 | A variation on that pattern is a control structure that doesn't need a master worker but where the workers coordinate among themselves about sending a message to the master when some condition is met. The "asymmetric barrier" in the demo section is such a control structure. 205 | 206 | The specification allows the browser to deny `wait` on the main thread, and it is expected that most browsers will eventually do so. A denied `wait` throws an exception. 207 | 208 | ### Don't mix atomic and non-atomic accesses 209 | 210 | It is certainly possible to access the same array element safely using both atomic and non-atomic accesses, but usually only in limited situations. In practice, it is best if any given shared array element is accessed either atomically or non-atomically; atomic elements should be used as synchronization variables and simple communication channels, while non-atomic elements can be used for larger amounts of data. 211 | 212 | ### The cost of shared memory 213 | 214 | How expensive is a SharedArrayBuffer? In the current Firefox implementation it is fairly expensive (the objects are multiples of 4KiB in size and at least 8KiB, and we sometimes reserve more address space), because we have been assuming that the easiest way to use shared memory is to allocate a few fairly large shared objects that are then partitioned by the application using multiple views. (In a sense this is a self-fulfilling prophecy because it is awkward to transfer shared objects among agents.) Other implementations could choose different strategies, encouraging the use of small objects (one per shared application object, say). 215 | 216 | ## Notes on current implementations (January 2016) 217 | 218 | Firefox and Chrome both implement the proposed API. 219 | 220 | * In Firefox the API is enabled by default in Nightly, and starting with Firefox 46 it can be enabled in Aurora, Developer Edition, Beta, and Release by setting `javascript.options.shared_memory` to true in `about:config`. Firefox denies `wait` on the main thread. 221 | * In Chrome the API is off by default and can be enabled by command line options (to be documented). 222 | 223 | In Firefox there is a per-domain limit on the number of workers that defaults to 20. Once the limit is exceeded new workers in the domain will be create but will remain un-started. A program that `wait`s on an un-started worker will usually deadlock. (Arguably the limitation is a violation of the WebWorkers spec, see [this WHATWG bug that aims to clarify that](https://www.w3.org/Bugs/Public/show_bug.cgi?id=29039).) 224 | 225 | -------------------------------------------------------------------------------- /asmjs/asmjs_spec.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | ECMAScript Shared Memory and Atomics - asm.js addenda 8 | 9 | 10 | 11 | 12 |

ECMAScript Shared Memory and Atomics - asm.js addenda

13 |

Revised: 2016-01-22

14 | 15 | 16 |

Introduction

17 | 18 |

This document defines the changes and amendments to asm.js 19 | semantics to support 20 | the proposal for ECMAScript shared memory and atomics. 21 | Please refer to that document for full information about the semantics.

22 | 23 |

This specification is a restatement of 24 | an earlier work, 25 | along with later bug fixes. The earlier work is obsolete.

26 | 27 |

Changelog:

28 | 34 |
35 | 36 | 37 |

Terminology

38 |

An "integer-typed array" is one of the ~TypedArray~ views ~Int8Array~, ~Uint8Array~, ~Int16Array~, ~Uint16Array~, ~Int32Array~, and ~Uint32Array~.

39 | 40 |

A "float-typed array" is one of the TypedArray views ~Float32Array~ and ~Float64Array~.

41 |
42 | 43 | 44 |

Views

45 | 46 |

The heap memory passed to the asm.js module at link time must be 47 | a ~SharedArrayBuffer~ if and only if the module references the 48 | ~Atomics~ object (see below). If this constraint fails then a 49 | soft link error ensues and execution falls back to non-asm.js, cf 50 | the asm.js spec section 7.

51 | 52 |
53 | 54 | 55 |

Atomics

56 | 57 |

There is a new known stdlib object ~Atomics~. This object 58 | provides a number of known, static methods, a subset of the 59 | methods provided in the full specification. The atomic operations 60 | have the same dynamic semantics in asm.js as in full JS.

61 | 62 | 63 |

The following Atomics names are not available as intrinsics in asm.js:

64 | 72 | 73 |

The futex methods can be accessed through the FFI (with the 74 | heap passed implicitly) and the operation result values can be 75 | expanded into constant values, as they have known values.

76 | 77 |
78 | 79 | 80 |

Atomics.load(view, index)

81 | 82 | 83 |

Static constraints

84 | 85 |

The _view_ must name an integer-typed array mapped onto 86 | shared memory.

87 | 88 |

The _index_ must be an expression of static type Intish. If 89 | the element byte size of _view_ is not 1 then _index_ must have 90 | the form _C_, where _C_ is a constant, or the form _E>>K_, where 91 | _E_ is some expression and _K_ is a constant that is the 92 | log-base-2 of the element byte size of _view_.

93 | 94 |

The result type of Atomics.load is Int.

95 | 96 | 97 |

Treating atomic accesses as "syntax" rather than as 98 | "calls" -- ie, requiring a shift for the index expression -- 99 | fits in with how they will be used and allows for 100 | simplifictions in code generation.

101 |
102 | 103 |
104 | 105 |
106 | 107 | 108 |

Atomics.store(view, index, value)

109 | 110 | 111 |

Static constraints

112 |

The _view_ and _index_ arguments are constrained as for Atomics.load.

113 | 114 |

The _value_ must be an expression of static type Intish.

115 | 116 |

The result type of Atomics.store is the actual type of _value_.

117 |
118 | 119 |
120 | 121 | 122 |

Atomics.exchange(view, index, value)

123 | 124 | 125 |

Static constraints

126 |

The _view_, _index_, and _value_ arguments are constrained as for Atomics.store.

127 | 128 |

The result type of Atomics.exchange is Int.

129 |
130 | 131 |
132 | 133 | 134 |

Atomics.add(view, index, value)

135 | 136 |

As for Atomics.exchange.

137 | 138 |
139 | 140 | 141 |

Atomics.sub(view, index, value)

142 | 143 |

As for Atomics.exchange.

144 | 145 |
146 | 147 | 148 |

Atomics.and(view, index, value)

149 | 150 |

As for Atomics.exchange.

151 | 152 |
153 | 154 | 155 |

Atomics.or(view, index, value)

156 | 157 |

As for Atomics.exchange.

158 | 159 |
160 | 161 | 162 |

Atomics.xor(view, index, value)

163 | 164 |

As for Atomics.exchange.

165 | 166 |
167 | 168 | 169 |

Atomics.compareExchange(view, index, expected, replacement)

170 | 171 | 172 |

Static constraints

173 |

The _view_ and _index_ arguments are constrained as for Atomics.load.

174 | 175 |

The _expected_ and _replacement_ arguments must be 176 | expressions of static type Intish.

177 | 178 |

The result type of Atomics.compareExchange is Int.

179 |
180 | 181 |
182 | 183 | 184 |

Atomics.fence()

185 | 186 |

Implements a full memory barrier.

187 | 188 | 189 |

Atomics.fence() is no longer a part of the Shared Memory and 190 | Atomics specification and we do not expect to reintroduce it. 191 | It is implemented in Firefox, for now, but will likely be removed.

192 |
193 | 194 | 195 |

Static constraints

196 | 197 |

The result type of Atomics.fence is void.

198 | 199 |
200 | 201 |
202 | 203 | 204 |

Atomics.isLockFree(size)

205 | 206 | 207 |

Static constraints

208 |

The _size_ argument must be an integer constant.

209 | 210 |

The result type of Atomics.isLockFree is Int, a boolean value.

211 | 212 | 213 |

Again, treating Atomics.isLockFree as syntax rather than 214 | as a call fits in with how it will be used and provides 215 | guarantees that it will be resolved at compile time.

216 |
217 | 218 |
219 | 220 |
221 | 222 |
223 | 224 | -------------------------------------------------------------------------------- /dom/dom_spec.html: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | ECMAScript Shared Memory and Atomics - DOM addenda 8 | 9 | 10 | 11 | 12 |

ECMAScript Shared Memory and Atomics - DOM addenda

13 |

Revised: 2016-08-03

14 | 15 | 16 |

Introduction

17 | 18 |

For Shared Memory and Atomics to be useful in the browser we must modify some DOM APIs to work with shared memory. At a minimum we need a method for sharing a SharedArrayBuffer among agents, but it is also useful to allow some WebGL APIs to operate on shared memory so as to avoid the overhead of copying large data arrays.

19 | 20 |

TODO - mine information from older spec docs and tickets and discussions to go here

21 | 22 |
23 | 24 | 25 |

Agents and Agent Clusters

26 | 27 | 28 |

Agent Cluster Types

29 | 30 |

A normal agent cluster shall comprise a single HTML document and its direct (and indirect, if nested workers are supported) Web Worker children.

31 | 32 | 33 |

Thus a document within a frame element is never in the same agent cluster as its parent document. The reason for this is partly that it is a conservative design that can be loosened later, and also that it is possible for the outer and inner documents to be in different content processes, making sharing memory difficult.

34 |
35 | 36 |

A special agent cluster is any agent cluster other than a normal agent cluster. Every special agent cluster shall comprise only a single agent.

37 | 38 | 39 |

Thus SharedWorkers and ServiceWorkers are agent clusters by themselves. The reason for this is largely that a normal agent cluster can be suspended or removed without SharedWorkers and ServiceWorkers being aware of that, and if the latter share memory with the cluster a deadlock can ensue as a shared-memory communication from the cluster is forever delayed. A secondary reason is that eg a ServiceWorker can be in a different content process from the documents using it, making sharing memory difficult.

40 |
41 | 42 | 43 |

(Spec draft note) If an agent like a SharedWorker can create its own WebWorkers then we really would like those to be within the agent cluster of the SharedWorker. But the WHATWG spec seems mum on nested workers in general.

44 |
45 | 46 |
47 | 48 | 49 |

Worker creation

50 | 51 |

When a web worker is created the embedding may delay actually starting the worker until the creating agent returns to its event loop.

52 | 53 | 54 |

Firefox has that constraint, and the architecture is unlikely to change. It is a possible source of non-portability that other browsers may be able to start the worker immediately.

55 |

In contrast, the WHATWG spec requires the worker to be started immediately (steps 10 and 11 of the worker creation algorithm).

56 |
57 | 58 |

If a web worker cannot be created (for example, because a thread cannot be created for it) then an error must be signaled by firing an Error event at the creating agent (as for failing to load the URL for the worker).

59 |
60 | 61 | 62 |

Worker forward progress

63 | 64 |

Each web worker shall run on a dedicated preemptable thread ("real thread") in order to guarantee forward progress.

65 | 66 |
67 | 68 | 69 |

Worker termination

70 | 71 |

If an agent in a cluster is terminated by the implementation then the entire agent cluster it is part of shall be terminated.

72 | 73 | 74 |

That behavior is chosen as there exists no reliable termination signaling mechanism today. The SAB proposal requires either termination en masse or a signaling mechanism.

75 | 76 |

Current browsers probably do not randomly terminate workers, but terminate them only when the page they belong to is removed from the browser (purged from the tab's history, or the tab is closed). Thus current browsers probably get this right already.

77 |
78 |
79 | 80 | 81 | 82 |

postMessage and structured cloning

83 | 84 |

The exclusive method for causing memory to be shared between agents shall be to send a SharedArrayBuffer from one agent in the cluster to another using `postMessage` and structured cloning.

85 | 86 |

The structured clone algorithm accepts SharedArrayBuffers and TypedArrays mapped onto SharedArrayBuffers. In both cases, the SharedArrayBuffer object is transmitted to the receiver resulting in a new, private SharedArrayBuffer object in the receiving agent (just as for ArrayBuffer). However, the Shared Data Block referenced by the two SharedArrayBuffer objects is the same data block, and a side effect to the block in one agent will eventually become visible in the other agent, following the semantics in the ES262 proposal.

87 | 88 |

A SharedArrayBuffer is not a Transferable object in the sense of HTML.

89 | 90 | 91 |

Thus if a SharedArrayBuffer object is present in the transfer list then `postMessage` will throw a DataCloneError.

92 | 93 |

In draft versions of the Shared Memory and Atomics spefification the SharedArrayBuffer was required to be present in the transfer list in the `postMessage` call.

94 |
95 | 96 |

If the sender and receiver are not part of the same agent cluster then then the receiver shall receive a SharedArrayBuffer object that references a new Shared Data Block with byte length zero and not the Shared Data Block that was sent.

97 | 98 | 99 |

The sender can't tell if the message is sent to a valid receiver since it may be sending on a MessageChannel that is not connected with a receiver.

100 | 101 |

(Spec draft note) Instead of receiving a zero-length block the receiver could receive some sort of error.

102 |
103 | 104 |
105 | 106 | 107 |

WebGL APIs

108 | 109 |

Modify the WebGL 1.0 spec as follows.

110 | 111 |

Change the type of `ArrayBufferView` to be a view whose buffer is exclusively an ArrayBuffer, ie, not a SharedArrayBuffer.

112 | 113 | 114 |

That probably depends on some kind of predicate mechanism, TBD, but the concept is easy enough, it is just an additional run-time check. A reasonable implementation of the check is just a load/compare/conditional branch triplet.

115 |
116 | 117 |

Introduce a type `ArrayBufferViewMaybeShared` to be any view type, ie, equivalent to the old definition of `ArrayBufferView`.

118 | 119 |

Introduce a type `BufferDataSourceMaybeShared`:

120 |

`typedef (ArrayBuffer or SharedArrayBuffer or ArrayBufferViewMaybeShared) BufferDataSourceMaybeShared`

121 | 122 |

Replace every occurrence of `BufferDataSource` in signatures with `BufferDataSourceMaybeShared`, ie, in the signatures of these functions: `bufferData`, `bufferSubData`.

123 | 124 |

Replace every occurrence of `ArrayBufferView` in signatures with `ArrayBufferViewMaybeShared`, ie in the signatures of these functions: `compressedTexImage2D`, `compressedTexSubImage2D`, `readPixels`, `texImage2D`, `texSubImage2D`.

125 | 126 |
127 | -------------------------------------------------------------------------------- /format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # This script will create tc39/shmem.html from the spec sources, and also 4 | # create asmjs/asmjs_shmem.html from the ditto sources. 5 | # 6 | # You need to: 7 | # 8 | # Install ecmarkup: 9 | # sudo npm install -g ecmarkup 10 | # 11 | # ecmarkup 1.6 or greater is required. 12 | 13 | ecmarkup tc39/spec.html tc39/shmem.html 14 | ecmarkup asmjs/asmjs_spec.html asmjs/asmjs_shmem.html 15 | ecmarkup dom/dom_spec.html dom/dom_shmem.html 16 | -------------------------------------------------------------------------------- /historical/README.md: -------------------------------------------------------------------------------- 1 | The archived materials come in pairs of files: .odt and .pdf, 2 | reflecting a problem with exporting the content from Google Docs 3 | properly. 4 | 5 | The .odt file preserves the comment threads from Google Docs, but it 6 | preserves *all* comment threads, including the closed ones. This is 7 | not ideal, because most comment threads were closed on most of these 8 | specs. 9 | 10 | The .pdf file does not preserve the comment threads. This is not 11 | ideal, because not all threads were closed. 12 | 13 | In general (but possibly not uniformly), the open comment threads were 14 | moved into the repository as open issues when the specs were moved to 15 | github. 16 | -------------------------------------------------------------------------------- /historical/SharedMemoryAtomicsLocks-asm.js-additions.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/historical/SharedMemoryAtomicsLocks-asm.js-additions.odt -------------------------------------------------------------------------------- /historical/SharedMemoryAtomicsLocks-asm.js-additions.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/historical/SharedMemoryAtomicsLocks-asm.js-additions.pdf -------------------------------------------------------------------------------- /historical/Spec_JavaScriptSharedMemoryAtomicsandLocks.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/historical/Spec_JavaScriptSharedMemoryAtomicsandLocks.odt -------------------------------------------------------------------------------- /historical/Spec_JavaScriptSharedMemoryAtomicsandLocks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/historical/Spec_JavaScriptSharedMemoryAtomicsandLocks.pdf -------------------------------------------------------------------------------- /historical/Synchronic.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/historical/Synchronic.odt -------------------------------------------------------------------------------- /historical/Synchronic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/historical/Synchronic.pdf -------------------------------------------------------------------------------- /issues/FutexWaitOnMainThread.md: -------------------------------------------------------------------------------- 1 | # Shared memory: Blocking on the main thread 2 | 3 | lhansen@mozilla.com / updated 2016-01-12 4 | 5 | ## Introduction 6 | 7 | At the moment, the spec allows `futexWait` to throw an exception if 8 | called on the browser's main thread (and Firefox implements that). 9 | The natural response of the application developer will be to use a 10 | spinloop to implement blocking, and we're no better off. In this case 11 | we can either (a) further curtail features, inevitably culminating 12 | with the banishing of shared memory views from the main thread or (b) 13 | provide code, minimally libraries but if necessary additional 14 | primitive APIs, to make it easy for programs to avoid blocking on the 15 | main thread. I favor the latter, it imposes fewer restrictions and is 16 | good enough. 17 | 18 | The general pattern will be that while workers can block with 19 | `futexWait` and will be woken when another agent calls `futexWake`, the 20 | master (the main thread) must register a callback and wait for it to 21 | be triggered by a worker. To simplify code, the callback can be 22 | packaged with async/await when the pattern is "wait for a specific 23 | resouce"; when the pattern is "wait for one of several conditions that 24 | may or may not occur" a straight callback may be simpler. 25 | 26 | Should we be thinking of this in terms of the signaling, ie, waking 27 | from a `futexWait` in the worker and receiving the callback in the 28 | master, or should we be thinking of this in terms of specific use 29 | cases, eg, critical sections, atomic cells, monitor objects, 30 | producer-consumer queues, and so on? There needn't be a conflict, but 31 | a general signaling mechanism may in practice be less useful than 32 | multiple use-case-specific mechanisms. 33 | 34 | So the goal of this exercise is in my view two things: 35 | 36 | * to enumerate some suitable master/worker patterns (use cases), and 37 | * to determine if those patterns can be implemented efficiently 38 | without additional features or, if not, figure out the new 39 | primitives that are required. 40 | 41 | ## Findings - Functionality 42 | 43 | After some experimentation it is clear that it is possible and 44 | reasonably practical to build some "asymmetric" data structures in JS 45 | using only a `futexWait` that can block on a worker thread, not on the 46 | main thread, and using Message events to signal the main thread. 47 | 48 | These data structures are described later, but in summary: Primitive 49 | data structures that can be built fairly easily are synchronics 50 | (shared atomic cells where updates trigger inter-thread signals) and 51 | barrier synchs. Using synchronics we can further build bounded 52 | unidirectional queues and simple latches, while barrier synchs or 53 | queues can be used to build data-parallel computation frameworks. 54 | These data structures have high-bandwidth worker-to-worker and 55 | master-to-worker communication; worker-to-master communication is 56 | slower due to message passing but that is usually OK. 57 | 58 | That is, for basic functionality it is almost certainly not necessary 59 | to add another primitive, such as the proposed `futexWaitCallback`, 60 | which would trigger a callback in the master in response to a 61 | `futexWake` in a worker. 62 | 63 | (Using the Message event is not great for software modularity, but 64 | appears workable for the time being.) 65 | 66 | ## Findings - Performance 67 | 68 | Experience shows that communiction bandwidth can be increased 69 | significantly if the agent that is waiting for a signal does not 70 | immediately block (or, for the master, immediately return to its event 71 | loop) but instead busy-waits or micro-waits "briefly" to reduce the 72 | wait/wake latency that comes from blocking. 73 | 74 | Such busy-waiting -- how to do it, for how long to do it -- is usually 75 | system-specific. For C++, there is a pending proposal to add 76 | "synchronic" objects that are atomic shared cells that can be used for 77 | efficient signalling between threads. Synchronics are higher level 78 | than Futexes but lower level than locks, and they would incorporate 79 | system-dependent knowledge of busy-waiting and other optimizations. 80 | 81 | One can implement synchronics in JS using Futexes, but incorporating 82 | the desirable optimizations is hacky at best. There are a couple of 83 | approaches to fixing that. 84 | 85 | ### Micro-wait plus futexWaitCallback 86 | 87 | One approach to better performance is to allow `futexWait` to wait for 88 | a short system-dependent time even on the main thread, consider eg 89 | 90 | ```js 91 | Atomics.futexWait(..., Atomics.MICRO_WAIT) 92 | ``` 93 | 94 | which might wait for a very short time in a way that is not 95 | detrimental to system performance before timing out or waking up from 96 | receiving a wakeup signal. 97 | 98 | As the micro-wait would be allowed on the main thread, a worker could 99 | use `futexWake` to signal the main thread. To avoid that the signal 100 | is lost if the signal is in transit when the main thread times out, 101 | however, we would need something like `futexWaitCallback` on the main 102 | thread. 103 | 104 | ### Primitive "synchronic" functionality 105 | 106 | Another approach to better performance is to add an API that takes us 107 | toward native synchronics. (I don't yet know if Futexes would still 108 | be necessary for certain low-level applications or not.) 109 | 110 | When waiting on a Synchronic the waiter specifies the value that it 111 | expects the cell to take on or not take on before it wants to be 112 | woken. This makes Synchronics easier to use than Futexes for many 113 | applications, and makes them amenable to built-in optimization: 114 | wakeups need in principle only happen when they are called for, while 115 | with "bare" futexes wakeups will have to happen for every waiter to 116 | re-check its condition. 117 | 118 | Synchronics have a more complicated API than Futexes: a Futex needs 119 | only one int32 cell; a synchronic needs information about the 120 | condition, the condition value, and in practice space for coordination 121 | data, which may be of system-dependent size. That space probably 122 | can't be system-allocated, it has to be provided in shared memory by 123 | the client application, and the application must manage that memory. 124 | 125 | For Synchronics, as for `futexWaitCallback`, a wakeup on the main 126 | thread must take the form of an event or callback. 127 | 128 | ## Data structures 129 | 130 | 131 | ### Asymmetric Synchronics 132 | 133 | [Implementation](https://github.com/lars-t-hansen/parlib-simple/blob/master/src/asymmetric-synchronic.js); 134 | [Test/sample html](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-synchronic.html); 135 | [Test/sample master code](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-synchronic-master.js); 136 | [Test/sample worker code](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-worker.js). 137 | 138 | The synchronic models a simple atomic shared cell with the usual suite 139 | of accessors: load, store, compareExchange, add, and so on. (The 140 | sample implementation supports only int32 but this is easily fixed.) 141 | In addition, there are methods for listening for updates: 142 | `waitForUpdate`, `waitUntilEquals`, `waitUntilNotEquals`. In a worker, the 143 | listening methods can block. In a master, a wait must return to the 144 | event loop and receive a callback later, and the methods are called 145 | `callbackWhenUpdated`, `callbackWhenNotEqual`, `callbackWhenEqual` instead, 146 | and there's a simple protocol for handling callbacks that are called 147 | immediately and for handling timeouts. 148 | 149 | With a synchronic, a (blocking) lock is quite simple: 150 | 151 | ```js 152 | while (s.compareExchange(0, 1) == 1) 153 | s.waitUntilEquals(0); 154 | ... 155 | s.store(0); 156 | ``` 157 | 158 | Importantly, if two threads are communicating at a high rate a waiting 159 | primitive can incorporate certain busy-wait optimizations to avoid 160 | blocking the thread immediately, and this can increase communication 161 | bandwidth significantly. 162 | 163 | While in principle the signal condition can be checked by always 164 | waking the waiter and having it re-check the condition, it is also 165 | possible to check the condition in the signaling thread, avoiding 166 | unnecessary thread wakeups. That might be especially appealing if the 167 | signal is being sent to the main thread, where a wakeup is an event 168 | dispatch. A native implementation would have an advantage here as the 169 | mechanism could be implemented on the main thread but outside the 170 | event handling machinery. 171 | 172 | A subtle point about asymmetric synchronics is that the master is 173 | really cooperatively multithreaded, or, if you like, it can have any 174 | number of callbacks waiting for an update to a cell. The sample 175 | implementation handles this properly but this is another case that can 176 | benefit from a low-level optimization where the updating worker only 177 | sends a signal if there's a call for it. 178 | 179 | Finally, though these synchronics handle a nonblocking master, they 180 | are efficient if used for just worker-to-worker communication. 181 | 182 | 183 | ### Asymmetric Barriers 184 | 185 | [Implementation](https://github.com/lars-t-hansen/parlib-simple/blob/master/src/asymmetric-barrier.js); 186 | [Test/sample html](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-barrier.html); 187 | [Test/sample master code](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-barrier-master.js); 188 | [Test/sample worker code](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-barrier-worker.js). 189 | 190 | These barriers allow the master to know when all the workers are 191 | stopped: when all workers are in the barrier the master is signaled, 192 | and the master can then start all the workers when it is done with its 193 | critical section. 194 | 195 | The barrier could have been based on synchronics but predate the 196 | synchronic implementation and use simple futex-based cooperation among 197 | the workers along with a postMessage to signal the master. For as 198 | simple as the barrier implementation is, the implementation had a race 199 | condition that required the addition of a seqLock to fix; this only 200 | illustrates how hard futexes are to use properly. A synchronics-based 201 | implementation would have been simpler. 202 | 203 | 204 | ### Unidirectional integer-bundle queues 205 | 206 | [Implementation](https://github.com/lars-t-hansen/parlib-simple/blob/master/src/asymmetric-intqueue.js); 207 | [Test/sample html](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-intqueue-m2w.html); 208 | [Test/sample master code](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-intqueue-m2w-master.js); 209 | [Test/sample worker code](https://github.com/lars-t-hansen/parlib-simple/blob/master/test/test-asymmetric-intqueue-m2w-worker.js). 210 | 211 | These integer queues transmit bundles of int32 data and are useful for 212 | marshaled data, for example. They are built on synchronics (both for 213 | signaling availability and space, and for controlling a critical 214 | section among concurrent workers) and adopt the synchronics' API: 215 | workers can perform blocking put and take, while the main thread has 216 | nonblocking `callbackWhenCanPut` and `callbackWhenCanTake` methods 217 | along with `putOrFail` and `takeOrFail`. 218 | 219 | Being built on synchronics, the queues also handle any number of 220 | "threads" in the master. 221 | 222 | 223 | ### Parallel computation frameworks 224 | 225 | [Implementation](https://github.com/lars-t-hansen/parlib-simple/blob/master/src/par.js); 226 | [Sample html](https://github.com/lars-t-hansen/parlib-simple/blob/master/demo/mandelbrot-animation2/mandelbrot.html); 227 | [Sample master js](https://github.com/lars-t-hansen/parlib-simple/blob/master/demo/mandelbrot-animation2/mandelbrot-master.js); 228 | [Sample worker js](https://github.com/lars-t-hansen/parlib-simple/blob/master/demo/mandelbrot-animation2/mandelbrot-worker.js). 229 | 230 | This is a simple data-parallel framework with work distribution and 231 | result collection. At the moment it is built on the asymmetric 232 | barrier as well as a custom shared-memory queue (it could easily use 233 | the integer-bundle queues above, but the code predates those queues). 234 | -------------------------------------------------------------------------------- /issues/MemoryModelConsequences.md: -------------------------------------------------------------------------------- 1 | [[[ 2 | Cut from the spec text at GetValueFromBuffer(): 3 | 4 | This restricts compiler optimizations as follows. If a program loads a value, and then uses the loaded value several places, an ECMAScript implementation must not re-load the value for any of the uses even if it can prove the agent does not overwrite the value in memory. It must also prove that no concurrent agent overwrites the value. 5 | 6 | @titzer says that is wrong (Issue #43). Although I think his comment was confused. 7 | 8 | But this is exactly the security issue that was debated in another thread: reloading a value after a security check breaks the security of that value. 9 | ]]] 10 | 11 | [[[ 12 | Also cut, from the memory model: 13 | 14 | 15 |

Also see the NOTE on GetValueFromBuffer() that clarifies 16 | that a compiler may not generate re-loads from shared 17 | memory.

18 | ]]] 19 | 20 | What does it mean for the shared memory model to leak into the spec? 21 | 22 | "Only with data races do optimizations become visible to the program." 23 | 24 | This is mostly about data races, I think - given that we're 25 | sequentially consistent absent data races, confusing behavior is a 26 | result of counterintuitive results about races. (For example the racy 27 | busy-wait loop.) That a read from a racy location can yield garbage 28 | and that that garbage can be any type-safe value is confusing, people 29 | don't think of garbage as "we skip the reading altogether and reuse 30 | the value we read last time". 31 | 32 | (But is it *only* about data races? Consider the buggy synchronization 33 | for the barrier, that required a seqLock to fix. Was that code racy? 34 | Probably not. It just did not perform inter-thread synchronization 35 | properly. Is this a normal bug or a new kind of bug?) 36 | 37 | Right now races can be hidden by syntax because we use TypedArray also 38 | for shared memory. That is, the semantic "leak" is deep in the 39 | language, at the memory access level and as (declaratively) specified 40 | by the memory model. 41 | 42 | If, instead, shared memory was accessed entirely along another path 43 | then the "leak" would be clearly set apart from existing machinery, 44 | and it would be obvious that code that does not call the shared memory 45 | primitives does not access shared memory and is not subject to racy 46 | behavior. 47 | 48 | The question is how much that is worth. Suppose shared memory use is 49 | packaged in a library but has some code that turns out to be racy. 50 | This raciness may or will spread to the client code. But is this more 51 | than just a bug? It is, when values that should not possibly exist 52 | come back as return values from the library. But again, is that more 53 | than just a bug? (Especially in a dynamic language.) 54 | 55 | -- 56 | 57 | How are the language semantics affected by races? 58 | 59 | - GetValueFromBuffer may return garbage 60 | 61 | - SetValueInBuffer may store garbage 62 | 63 | - The garbage may not actually be stable (for a period that's very 64 | hard to pin down) 65 | 66 | - The garbage is always type safe though: NaNs are still canonicalized 67 | 68 | - Once garbage is in memory it spreads: eg, a branch based on a 69 | garbage value may go either way, an array store using a garbage 70 | index may store into any slot (or out of bounds) 71 | 72 | - Multiple reads of the same garbage may read different values, which 73 | affects program logic 74 | 75 | We can perhaps *model* this by saying, in GetValueFromBuffer, that if 76 | the location has been subject to a race then random byte values are 77 | read by steps 9a, 10a, and 11a. 78 | 79 | But that requires pinning down whether a particular location has been 80 | subject to a race. The union-of-locations rule is a good start but is 81 | it reliable? 82 | 83 | If an operation uses racy data, does the operation itself become part 84 | of the race? Are data it stores also racy? I would be inclined to 85 | say not, since racy values that are read are type-safe and once read 86 | they stay read, except that re-reading is an issue: a value that is 87 | re-read (by the compiler) may read a different value. 88 | 89 | And what about reordering in the CPU? 90 | 91 | When does the race end? Suppose N writes race on a location L; at 92 | some (global) time the last of those has completed. How does the race 93 | end? A regular read to L will be racy. An atomic store to L will 94 | also be racy (races with a non-atomic store). 95 | 96 | The race therefore ends only when the threads synchronize on other 97 | locations than L, creating a relationship between the racy accesses on 98 | L and those synchronizations, and decoupling accesses to L after the 99 | synchronization from the ones before. 100 | 101 | The threads don't all have to synchronize on the same other-than-L 102 | location because seqcst - the syncs are ordered, the order does not 103 | matter. But they must all *synchronize*, I think, that were involved 104 | in the race on L, it's not enough to interact with an atomic var. 105 | Suppose two threads T1 and T2 and two sync locations S1 and S2. They 106 | race to write on L. Now T1 writes S1 and T2 writes S2. Those writes 107 | are ordered, and both happen after the race on L, and both threads can 108 | therefore assume that the race is over - but only after both events. 109 | But since T1 and T2 are not aware of the other's event, they never 110 | know when that is. One must still signal the other (arguaby they must 111 | perhaps signal each other). 112 | 113 | Thus: the race is over once all the racing threads have synchronized 114 | properly with each other after the race, which means created a 115 | happened-before relationship that involves all of them both sending a 116 | signal and receiving a signal. 117 | 118 | Effectively this means a global atomic counter reaching N, where each 119 | thread counts up. 120 | 121 | 122 | 123 | Until the race is known to be over there's no telling what will be 124 | written to or read from a racy location. 125 | -------------------------------------------------------------------------------- /issues/TimingAttack.md: -------------------------------------------------------------------------------- 1 | # Shared memory: Side-channel information leaks 2 | 3 | lhansen@mozilla.com / updated 2016-01-24, minor changes 2016-07-20 4 | 5 | ## Introduction 6 | 7 | It is possible to create a high-resolution timer by using shared 8 | memory and a worker: the worker runs in a loop that increments a 9 | shared cell with an atomic operation, and whatever agent needs a clock 10 | just reads the cell. Yossi Oren has measured such a cell to have a 11 | 4ns resolution on current (2015) hardware. The clock will be a little 12 | noisy as a result of system behavior but probably has much higher 13 | resolution in practice than the attenuated `performance.now()` timer, 14 | which has 5μs resolution. 15 | 16 | A number of side channel attacks need a high-resolution timing source 17 | to work. (This is why `performance.now()` resolution has been reduced.) 18 | There are several examples of such attacks in JS, including last-level 19 | cache sniffing to extract user behavior or user data [Oren, 1], row 20 | hammering to cause bit flips in memory on some types of hardware [Gruss et al, 2], 21 | and SVG/CSS attacks that can read pixels using transforms [Stone, 3] [Andrysco et al, 4]. In 22 | virtualized server environments, though not yet in JS, it has been 23 | possible to extract cryptographic keys for AES and RSA from the cache 24 | [Osvik et al, 5] [Inci et al, 6]. Several published papers focus on exfiltration of data as a high-value use of JS, wherein a local attacker process would signal data by accessing its memory and the JS code, having legitimate access to the network from within the browser, would detect those signals and upload the data. 25 | 26 | Attacks are also being launched against ARM [Lipp et al, 8] 27 | and there are ever-novel attack vectors [Gruss et al, 9]; the relevance 28 | of those attacks to JS is hazy, at this time. 29 | 30 | In these cases, a precise timer is needed to distinguish a fast 31 | operation from a slow operation. For the cache attacks and row 32 | hammering this is the time difference between a cache hit and a cache 33 | miss; for the SVG/CSS attacks it is the difference between a fast 34 | transform (the bit we're reading is set one way) and a slow one (it's 35 | set the other way). Without a fast timer these attacks are not 36 | effective. 37 | 38 | ## Mitigations and countermeasures 39 | 40 | ### Thread affinity 41 | 42 | The only reasonable "hidden" mitigation that's been proposed and that 43 | can be implemented by the browser vendors is to make sure that threads 44 | that share memory have affinity to the same CPU. In that case, the 45 | thread that generates the clock signal does not run in parallel with 46 | the thread that reads it, as they are timesliced on the same core. 47 | The clock signal is thus destroyed. (Or so the theory goes.) 48 | 49 | In favor of that mitigation is that it does not destroy the 50 | shared-memory feature; workers can still share memory, and can share 51 | the memory with their owning main thread. 52 | 53 | However, actual parallel execution is destroyed by setting the 54 | affinity in that manner, thus removing one of the main justifications 55 | for the feature in JS, and probably making programs that use shared 56 | memory less effective than programs that copy memory. In practice, 57 | requiring affinity may be a reasonable default if it can be changed 58 | easily when needed, or it may be a reasonable option for high-security 59 | environments such as Tor [Tor project, 7], but it is not a reasonable mitigation when 60 | shared-memory parallelism is actually needed. 61 | 62 | The affinity solution has another couple of problems: 63 | 64 | - Thread affinity has real teeth on Windows and Linux but is not well 65 | supported on Mac OS X (and may not be well supported on other 66 | platforms). 67 | - Browsers that have a single main thread that is the main thread for 68 | all or many tabs (as does Firefox at the moment) will end up forcing 69 | many worker threads onto the same core, if shared memory becomes popular on 70 | the web and affinity is enabled by default. 71 | 72 | ### Opt-in 73 | 74 | At the moment several browsers implement an opt-in scheme for plugin 75 | content. It may be possible to implement something similar for shared 76 | memory, whereby (say) the shared memory APIs are available but with 77 | thread affinity enabled by default, and some user action allows 78 | threads to run free on multiple cores. Like the plugin content, it 79 | could be a per-domain or per-tab permission, set once and for all or 80 | for a shorter duration. 81 | 82 | Opt-in is not a great solution because most users will not know what 83 | they opt in to, so it will be confusing, and the security will be 84 | questionable (most users run all plugins always when prompted). It's 85 | also not clear how the need to opt in would be communicated to the 86 | page. 87 | 88 | (Also, as discussed above, thread affinity is not necessarily controllable, 89 | and in practice the default might need to be that the shared-memory feature 90 | is not available, or that it is available in some attenuated form.) 91 | 92 | ## How bad is it? 93 | 94 | ### Known and potential impact 95 | 96 | We don't really know how bad this attack is. The published attacks in 97 | JS are not yet devastating, but attacks only get better. The web is 98 | vast; an attack that works against many computers and is delivered via 99 | an ad, say, can be very bad. Or the attack can go into a toolbox for 100 | targeted attacks (APT). 101 | 102 | In general, a web browser runs untrusted code and for that code to be 103 | able to steal information from the user is a big deal. 104 | 105 | 106 | ### New impact 107 | 108 | The thing is, it's not obvious that the attack provides a genuinely 109 | new capability to attackers. Consider the existing technologies on 110 | the Web that can be used to mount the cache attacks: 111 | 112 | - Flash Player (AS3 has shared memory) 113 | - Java 114 | - PNaCl / NaCl 115 | - browser extensions with native code 116 | - native messaging in Chrome 117 | - js-ctypes in Firefox 118 | - ActiveX in Internet Explorer 119 | - probably more 120 | 121 | In all cases those technologies do not need shared memory at all if 122 | they already have a precise clock for the attack, but absent a precise 123 | clock all do have shared memory and can build such a clock. 124 | 125 | Java and ActiveX were brought up already in 2005 by Osvik in his 126 | paper on attacking AES [5]. 127 | 128 | In addition, several of the published attacks do not appear to need 129 | the new clock: the rowhammer.js authors have said that the 5μs 130 | throttled clock is not a hindrance to them; Oren states that a 1μs 131 | clock is sufficient for cache sniffing, and such a clock can be built 132 | (by counting) from the 5μs clock. 133 | 134 | The shared-memory clock might affect the SVG/CSS attack, especially as 135 | implemented for exploiting subnormal timings, although that attack is 136 | not an issue at present. (In Firefox the computation was first made 137 | timing-independent and later pushed to the GPU.) 138 | 139 | None of the attacks appear to be aided by being able to allocate 140 | memory that is merely shared among the threads of the same browser 141 | process. (The attacks bring up "shared memory" as an attack vector, 142 | but this is in order to play memory mapping tricks.) 143 | 144 | 145 | ### Future impact 146 | 147 | In the future, if WebAssembly adds shared memory and threads (as it is 148 | expected it will) it will inevitably run into the same problem. 149 | 150 | 151 | ### Server-side impact 152 | 153 | node.js is not all that interesting in this context because it only 154 | runs trusted code. Clearly "trusted" is a matter of definition (do 155 | you know what you get with "require"?), but at least the code is not 156 | downloaded from the web through an ad or a random web page. 157 | 158 | 159 | ## The hardware problem 160 | 161 | The timing attacks are made possibly by what can only be described as 162 | hardware bugs: 163 | 164 | - Rowhammer is ultimately caused by a combination of DDR3 memory 165 | weaknesses coupled with aggressive (ie low) BIOS DRAM refresh rates. 166 | (Later also demonstrated on some DDR4 memory.) 167 | - Cache sniffing is arguably a problem with shared caches, a problem 168 | that is far from new but has now moved to the L3 cache in 169 | virtualized environments. (Later also demonstrated on some L1 caches.) 170 | 171 | Rowhammer is being addressed by adjusting the BIOS refresh rates (with a performance penalty) and 172 | by more resilient memory types. Cache sniffing is being addressed by 173 | cache partitioning (though it's unclear how good current attempts 174 | are). 175 | 176 | In the past, the cost of denormal floating-point operations has been 177 | used for information stealing with SVG filters, an attack that has 178 | been mitigated by changing timer resolution in the browsers but also 179 | by the CPU manufacturers addressing denormal timing (to some extent), 180 | or by having the hardware flush subnormals to zero. 181 | 182 | As time goes by, the hardware problems are mitigated, and new ones are 183 | introduced, eg, GPUs now support denormals, but they implement 184 | operations on denormals in microcode, making them useful timing 185 | channels. 186 | 187 | We should not let "the hardware problem" be a reason not to take the 188 | attacks seriously, but worrying about one particular type of clock, 189 | some particular hardware issues, and how they combine to enable these 190 | particular side channel leaks feels like a fairly narrow point of 191 | view. Other clocks and other hardware bugs will be found. (An 192 | experiment shows that a counting clock made from the 5μs timer can get 193 | pretty reliable 1μs resolution.) The problem is less the specific 194 | nature of these side channels than that sensitive computations are not 195 | properly insulated from the rest of the system. Admittedly, insulation 196 | often requires hardware and OS changes and not merely careful coding, 197 | but it is still where the actual problem is. 198 | 199 | ## References 200 | 201 | [1] [Yossef Oren et al, "The Spy in the Sandbox -- Practical Cache Attacks in Javascript"](http://arxiv.org/abs/1502.07373v2) 202 | 203 | [2] [Daniel Gruss et al, "Rowhammer.js: A Remote Software-Induced Fault Attack in JavaScript"](http://arxiv.org/abs/1507.06955v1) 204 | 205 | [3] [Paul Stone, "Pixel perfect timing attacks with HTML5"](http://contextis.co.uk/research/white-papers/pixel-perfect-timing-attacks-html5) 206 | 207 | [4] [Marc Andrysco et al, "On Submornal Floating point and abnormal timing](https://cseweb.ucsd.edu/~dkohlbre/papers/subnormal.pdf) 208 | 209 | [5] [Dag Arne Osvik et al, "Cache Attacks and Countermeasures: the Case of AES"](https://eprint.iacr.org/2005/271.pdf) 210 | 211 | [6] [Mehmet Sinan Inci et al, "Seriously, get off my cloud! Cross-VM RSA Key Recovery in a Public Cloud"](https://eprint.iacr.org/2015/898.pdf) 212 | 213 | [7] [Tor project: "High-precision timestamps in JS"](https://trac.torproject.org/projects/tor/ticket/17412) 214 | 215 | [8] [Moritz Lipp et al, "ARMageddon: Last-Level Cache Attacks on Mobile Devices"](http://arxiv.org/abs/1511.04897) 216 | 217 | [9] [Daniel Gruss et al, "Flush+Flush: A Fast and Stealthy Cache Attack"](http://arxiv.org/abs/1511.04594) 218 | -------------------------------------------------------------------------------- /issues/WhyNotJustAsmOrWasm.md: -------------------------------------------------------------------------------- 1 | (Raw notes, need cleanup.) 2 | 3 | Some tasks are best / only done in ES callouts 4 | Input and output, DOM access 5 | “Runtime” tasks 6 | 7 | Copy-in/copy-out at boundary too slow (WebGL) 8 | Thus ES wants some shmem access 9 | 10 | Also asm.js (= ES) can't polyfill for wasm 11 | 12 | Multicore computation in ES is valuable 13 | 14 | ES is the better language (GC, rich types) 15 | 16 | “wasm-only” does not remove races: 17 | ES can call wasm 18 | wasm can export racy accessor functions -------------------------------------------------------------------------------- /tc39/README.md: -------------------------------------------------------------------------------- 1 | spec.html is processed using ecmarkup into shmem.html, which is the 2 | final formatted spec. 3 | 4 | ecmarkup is available here: https://github.com/bterlson/ecmarkup. 5 | 6 | ecmarkup requires io.js, available here: https://iojs.org/en/index.html. 7 | 8 | ACKNOWLEDGEMENTS: The SIMD.js spec was very helpful in formatting the 9 | shared memory spec, see https://github.com/johnmccutchan/ecmascript_simd. 10 | -------------------------------------------------------------------------------- /tc39/agents-mar-2016.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/agents-mar-2016.odp -------------------------------------------------------------------------------- /tc39/agents-mar-2016.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/agents-mar-2016.pdf -------------------------------------------------------------------------------- /tc39/clippings.html: -------------------------------------------------------------------------------- 1 | 2 |

MemoryLock( sab, offset, size )

3 | 4 |

FIXME: This may not adequately specify global synchronization. It may be an error to attempt to operationalize this. But it's important to specify that overlapping but non-indentical atomic accesses to the same locations may actually be racy.

5 | 6 | 7 | 1. Assert: _sab_ is a SharedArrayBuffer 8 | 1. Assert: _offset_ ≥ 0 and _offset_ + _size_ ≤ _sab_.byteLength and _offset_ mod _size_ == 0 9 | 1. Assert: _size_ is 1, 2, or 4. 10 | 1. Assert: The calling agent's rights are cleared. 11 | 1. Let _G_ be the unique identifier for the memory of _sab_ 12 | 1. Wait until no other agent's access rights specify the triple (_G_, _offset_, _size_), then set the calling agent's rights to (_G_, _offset_, _size_) 13 | 14 | 15 | 16 |

MemoryLock communicates with other agents.

17 |
18 | 19 | 20 |

Atomicity is only guaranteed for same-sized accesses to the same memory locations, hence the attempt above at operationalizing that. But that may be a mistake.

21 |
22 | 23 |
24 | 25 | 26 |

MemoryUnlock()

27 | 28 | 1. Assert: The calling agent's rights are not cleared. 29 | 1. FIXME: Somehow specify that all local writes will be observed by other agents before they observe that the flag is ~false~. 30 | 1. Clear the calling agent's rights 31 | 32 | 33 |

MemoryUnlock communicates with other agents.

34 |
35 |
36 | 37 | 38 | 39 |

Ordinary and Exotic Object Behaviors (ES6 9)

40 | 41 |

Built-in Exotic Object Internal Methods and Slots (ES6 9.4)

42 | 43 |

Integer Indexed Exotic Objects (ES6 9.4.5)

44 |

FIXME: It's possible that the shared-memory case should be embedded in GetValueFromBuffer and SetValueInBuffer, since the latter have callers elsewhere.

45 | 46 |

IntegerIndexedElementGet ( O, index ) (ES6 9.4.5.8)

47 |

Replace 14 with the following algorithm:

48 | 49 | 1. If IsSharedMemory(_buffer_) is true, then: 50 | 1. Return GetValueFromSharedBuffer( _buffer_, _indexedPosition_, _elementType_ ) 51 | 1. Else 52 | 1. Return GetValueFromBuffer( _buffer_, _indexedPosition_, _elementType_ ) 53 | 54 |
55 | 56 | 57 |

IntegerIndexedElementSet ( O, index, value ) (ES6 9.4.5.9)

58 |

Replace step 16 with the following algorithm:

59 | 60 | 1. If IsSharedMemory(_buffer_) is true, then 61 | 1. Perform SetValueInSharedBuffer( _buffer_, _indexedPosition_, _elementType_, _numValue_ ) 62 | 1. Else: 63 | 1. Perform SetValueInBuffer( _buffer_, _indexedPosition_, _elementType_, _numValue_ ) 64 | 65 |
66 |
67 |
68 |
69 | 70 | 71 |

GetValueFromSharedBuffer ( arrayBuffer, byteIndex, type )

72 |

This is the same as ArrayBuffer's GetValueFromBuffer (ES6 24.1.1.5) except that:

73 | 79 |
80 | 81 | 82 |

SetValueInSharedBuffer ( arrayBuffer, byteIndex, type, value )

83 |

This is the same as ArrayBuffer's SetValueInBuffer (ES6 24.1.1.6) except that:

84 | 90 |
91 | 92 | 93 | 94 | 95 | 96 | A step in the algorithms below that is marked "With atomic access to ..." is known as an atomic step. There must be a single total order (shared by all the agents that are able to communicate through shared memory) in which atomic steps occur, known as the atomics order. If an atomic step R reads some bytes B0 through Bn in a SharedArrayBuffer, for each byte B it may return any of:

97 | 98 |

There is a relation happens-before on program steps that is defined as follows.

99 | 104 | 105 | 106 | Note, how do the futex sections figure into all of this? 107 | 108 | 109 |

A step in the algorithms below that is marked "With atomic access to ..." is known as an atomic step. There must be a single total order (shared by all the agents that are able to communicate through shared memory) in which atomic steps occur, known as the atomics order. If an atomic step R reads some bytes B0 through Bn in a SharedArrayBuffer, for each byte B it may return any of:

110 | 115 | 116 |

Notes:

117 | 123 | 124 |

If the two threads access the same shared memory cells , and at least one access is not an atomic operation 125 |

Atomic accesses to memory are totally ordered.

126 | 127 | 128 | 137 | 138 | 139 |

Atomics.loadUnordered( typedArray, index )

140 | 141 |

This is the same operation as Atomics.load, but where the observable memory effects of Atomics.load relative to operations before and after it are defined, Atomics.loadUnordered can be rearranged (in terms of memory effects) with unordered operations that precede or succeed it, including non-atomic operations. Atomics.loadUnordered can be implemented much more efficiently than Atomics.load on almost all platforms.

142 |
143 |
144 | 145 | 146 |

Atomics.storeUnordered( typedArray, index, value )

147 | 148 |

This is the same operation as Atomics.store, but where the observable memory effects of Atomics.store relative to operations before and after it are defined, Atomics.storeUnordered can be rearranged (in terms of memory effects) with unordered operations that precede or succeed it, including non-atomic operations. Atomics.storeUnordered can be implemented much more efficiently than Atomics.store on almost all platforms.

149 |
150 |
151 | 152 | 153 | -------------------------------------------------------------------------------- /tc39/presentation-jan-2016.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/presentation-jan-2016.odp -------------------------------------------------------------------------------- /tc39/presentation-jan-2016.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/presentation-jan-2016.pdf -------------------------------------------------------------------------------- /tc39/presentation-nov-2016.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/presentation-nov-2016.pdf -------------------------------------------------------------------------------- /tc39/presentation-sept-2015.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/presentation-sept-2015.odp -------------------------------------------------------------------------------- /tc39/presentation-sept-2015.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/presentation-sept-2015.pdf -------------------------------------------------------------------------------- /tc39/sharedmem-jul-2016.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/sharedmem-jul-2016.odp -------------------------------------------------------------------------------- /tc39/sharedmem-jul-2016.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/sharedmem-jul-2016.pdf -------------------------------------------------------------------------------- /tc39/sharedmem-mar-2016.odp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/sharedmem-mar-2016.odp -------------------------------------------------------------------------------- /tc39/sharedmem-mar-2016.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tc39/proposal-ecmascript-sharedmem/55dfee88564e075a68b98ff4116121f6161953f4/tc39/sharedmem-mar-2016.pdf -------------------------------------------------------------------------------- /test-html/LICENSE.md: -------------------------------------------------------------------------------- 1 | The intended license for the test-html/ subdirectory of the 2 | ecmacript_sharedmem repository is the BSD license. 3 | -------------------------------------------------------------------------------- /test-html/external/README.md: -------------------------------------------------------------------------------- 1 | Test case for sending SharedArrayBuffer to/from a ServiceWorker. 2 | 3 | The files in this directory are normally copied into the tests/ 4 | subdirectory in the gh_pages branch of this repository and then 5 | committed and pushed; they are then accessed via 6 | ../test_serviceWorkerNotShared.html, which links to the proper URL. 7 | -------------------------------------------------------------------------------- /test-html/external/serviceWorkerNotSharing_master.js: -------------------------------------------------------------------------------- 1 | if (!this.SharedArrayBuffer) 2 | msg("STOPPED: Can't run: No SharedArrayBuffer"); 3 | else if (!navigator.serviceWorker) 4 | msg("STOPPED: Can't run: No ServiceWorker"); 5 | else 6 | runTest(); 7 | 8 | function runTest() { 9 | var theWorker = null; 10 | var sab = new SharedArrayBuffer(236); // Non-random length 11 | 12 | navigator.serviceWorker.addEventListener('message', function (ev) { 13 | if (ev.data instanceof SharedArrayBuffer) { 14 | if (ev.data.byteLength == 200) { 15 | msg("OK: Master received SAB of length 200"); 16 | 17 | // Modify the memory we just received so that the worker can check. 18 | let ia2 = new Int32Array(ev.data); 19 | ia2[12] = 0xdeadbeef; 20 | ia2[33] = 0xcafebabe; 21 | } 22 | else 23 | msg("ERROR: Master received a SharedArrayBuffer instance with unexpected length=" + ev.data.byteLength); 24 | 25 | // The worker believed it got a shared buffer, or we wouldn't be here. 26 | // See if the worker modified the shared memory the master sent. 27 | let ia = new Int32Array(sab); 28 | if (ia[37] != (0xdeadbeef|0) || ia[42] != (0xcafebabe|0)) 29 | msg("ERROR: Worker did not properly update shared memory sent from master: " + 30 | ia[37].toString(16) + " " + ia[42].toString(16)); 31 | else 32 | msg("OK: Worker's modifications are visible in the master"); 33 | } 34 | else if (typeof ev.data == "string") 35 | msg(ev.data); 36 | else 37 | msg("ERROR: Master received unexpected data: " + ev.data); 38 | }); 39 | 40 | navigator.serviceWorker.register('/ecmascript_sharedmem/tests/serviceWorkerNotSharing_worker.js', 41 | { scope: '/ecmascript_sharedmem/tests/dummy/' }).then(function (reg) { 42 | if (reg.installing) 43 | theWorker = reg.installing; 44 | else if (reg.waiting) 45 | theWorker = reg.waiting; 46 | else if (reg.active) 47 | theWorker = reg.active; 48 | 49 | if (theWorker) { 50 | theWorker.postMessage("hello"); 51 | setTimeout(function () { 52 | try { 53 | msg("# Sending SAB of length 236 to worker"); 54 | theWorker.postMessage(sab, [sab]); 55 | msg("# Did send SAB to worker without exception "); 56 | } 57 | catch (e) { 58 | msg("EXCEPTION SEEN: postMessage from master to worker:\n" + e); 59 | } 60 | }, 1000); 61 | } 62 | }).catch(function (error) { 63 | msg("STOPPED: Can't run: Registration failed with " + error); 64 | }); 65 | } 66 | -------------------------------------------------------------------------------- /test-html/external/serviceWorkerNotSharing_worker.js: -------------------------------------------------------------------------------- 1 | onfetch = function (ev) { 2 | ev.respondWith(new Response("Hi there", { status: 200, statusText: "OK" })); 3 | } 4 | 5 | var theMaster = null; 6 | 7 | onmessage = function (ev) { 8 | if (ev.data === "hello") 9 | theMaster = ev.source; 10 | else { 11 | if (ev.data instanceof SharedArrayBuffer) { 12 | if (ev.data.byteLength == 236) { 13 | theMaster.postMessage("OK: Worker received SAB of length 236"); 14 | 15 | // Now update the supposedly shared memory so that the 16 | // master can check whether the memory is actually shared. 17 | // The master will perform the check once it receives a 18 | // message in return, below. 19 | 20 | let ia = new Int32Array(ev.data); 21 | ia[37] = 0xdeadbeef; 22 | ia[42] = 0xcafebabe; 23 | } 24 | else 25 | theMaster.postMessage("ERROR: ServiceWorker received a SharedArrayBuffer instance with unexpected length=" + 26 | ev.data.byteLength); 27 | } 28 | else 29 | theMaster.postMessage("ERROR: ServiceWorker received unexpected data: " + ev.data); 30 | setTimeout(() => sendToMaster(false), 1000); 31 | } 32 | } 33 | 34 | // Fallback: if nothing is received from the master, try to send a SAB 35 | // to it - to see if perhaps transmission is only blocked in one 36 | // direction. 37 | setTimeout(() => sendToMaster(true), 5000); 38 | 39 | var sent = false; 40 | 41 | function sendToMaster(didTimeout) { 42 | if (sent) 43 | return; 44 | sent = true; 45 | var sab = new SharedArrayBuffer(200); 46 | try { 47 | if (didTimeout) 48 | theMaster.postMessage("# Timed out waiting for a message from master"); 49 | theMaster.postMessage("# Sending SAB of length 200 to master"); 50 | theMaster.postMessage(sab, [sab]); 51 | theMaster.postMessage("# Did send SAB to master without exception"); 52 | setTimeout(function () { 53 | let ia = new Int32Array(sab); 54 | if (ia[12] != (0xdeadbeef|0) || ia[33] != (0xcafebabe|0)) 55 | theMaster.postMessage("ERROR: The master did not properly update the worker's shared memory" + 56 | ia[12].toString(16) + " " + ia[33].toString(16)); 57 | else 58 | theMaster.postMessage("OK: Master's modifications are visible in the worker"); 59 | }, 1000); 60 | } catch (e) { 61 | theMaster.postMessage("EXCEPTION SEEN: postMessage from worker to master: " + e); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /test-html/external/test_serviceWorkerNotSharing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests whether a ServiceWorker can receive and send shared memory 6 | 7 | 8 | 9 | 10 |

This tests whether a ServiceWorker can receive and send shared 11 | memory objects. Whether it should be able to do so depends on the 12 | embedding.

13 | 14 |

Output will appear below. In addition to informational and "OK" lines there are lines about exceptions and errors. Two forms of output are expected: 15 | 16 |

20 | 21 |

For any other type of output, consult the code.

22 | 23 |
24 | 25 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test-html/futexInterrupt-worker.js: -------------------------------------------------------------------------------- 1 | importScripts("worker-harness.js"); 2 | 3 | onmessage = 4 | function (ev) { 5 | var iab = new Int32Array(ev.data); 6 | Atomics.wait(iab, 0, 0, Number.POSITIVE_INFINITY); 7 | postMessage("THIS SHOULD NOT HAPPEN"); 8 | }; 9 | -------------------------------------------------------------------------------- /test-html/futexMainWakeup-worker.js: -------------------------------------------------------------------------------- 1 | importScripts("worker-harness.js"); 2 | 3 | var mem; 4 | 5 | onmessage = function (ev) { 6 | switch (ev.data[0]) { 7 | case "start": 8 | mem = new Int32Array(ev.data[1]); 9 | break; 10 | case "run": 11 | setTimeout(function () { 12 | postMessage("Worker: Waking it up now"); 13 | Atomics.wake(mem, 0, 1) 14 | }, ev.data[1]); 15 | break; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /test-html/futexTimeout_slave.js: -------------------------------------------------------------------------------- 1 | importScripts("worker-harness.js"); 2 | 3 | var mem; 4 | var WAITLOC; 5 | 6 | onmessage = 7 | function (ev) { 8 | try { 9 | switch (ev.data[0]) { 10 | case "start": 11 | mem = new Int32Array(ev.data[1]); 12 | WAITLOC = ev.data[2]; 13 | break; 14 | case "timeout": 15 | var [_, ms, result] = ev.data; 16 | postMessage(["msg", "Going into " + ms + "ms wait in the worker"]); 17 | var t0 = Date.now(); 18 | var r = Atomics.wait(mem, WAITLOC, 0, ms); 19 | postMessage(["msg", "Wait returned " + r]); 20 | var t1 = Date.now(); 21 | switch (r) { 22 | case "timed-out": 23 | case "not-equal": 24 | case "ok": 25 | postMessage([r, result, t1-t0]); 26 | break; 27 | default: 28 | postMessage(["failure", [result, r], t1-t0]); 29 | break; 30 | } 31 | break; 32 | default: 33 | postMessage("Bogus command: " + ev.data.join(",")); 34 | break; 35 | } 36 | } 37 | catch (e) { 38 | postMessage("Caught exception: " + e); 39 | } 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /test-html/futexWakeInInterrupt-worker.js: -------------------------------------------------------------------------------- 1 | importScripts("worker-harness.js"); 2 | 3 | onmessage = function (ev) { 4 | var [sab] = ev.data; 5 | var mem = new Int32Array(sab); 6 | 7 | setTimeout(function () { 8 | Atomics.wake(mem, 0, 1); 9 | }, 20000); 10 | } 11 | -------------------------------------------------------------------------------- /test-html/harness.js: -------------------------------------------------------------------------------- 1 | document.writeln("
"); 2 | 3 | function msg(s) { 4 | var d = document.createElement('div'); 5 | d.appendChild(document.createTextNode(s)); 6 | document.getElementById("scrool").appendChild(d); 7 | } 8 | 9 | // The spec allows individual threads to prevent blocking. In a 10 | // browser, this will be an issue only on the main thread. 11 | 12 | var canBlockOnThisThread = (function () { 13 | var mem = new Int32Array(new SharedArrayBuffer(4)); 14 | var didThrow = false; 15 | try { 16 | Atomics.wait(mem, 0, 0, 0); 17 | } 18 | catch (e) { 19 | didThrow = true; 20 | } 21 | return !didThrow; 22 | })(); 23 | -------------------------------------------------------------------------------- /test-html/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | In-browser test cases for shared memory and atomics 6 | 7 | 8 | 9 |

These test cases run as you load them and test that sharing restrictions are implemented correctly:

10 | 16 | 17 |

These test cases run as you load them and test misc inter-agent functionality:

18 | 23 | 24 |

These test cases require additional interaction to run:

25 | 32 | 33 |

These test cases require the browser or tab to be shut down:

34 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /test-html/sharedMemCriticalSection_defs.js: -------------------------------------------------------------------------------- 1 | // Parameters shared between the master and the workers. 2 | 3 | const doLock = true; // Set to false to see what happens without mutual exclusion 4 | 5 | const mutexLoc = 0; 6 | const statusLoc = 1; 7 | const arrayExtra = 2; 8 | const arrayElements = 1000; 9 | const arrayLo = arrayExtra; 10 | const arrayLim = arrayLo + arrayElements; 11 | const iterations = 1000; // Limit is 1023, for 10-bit fields 12 | const workers = 3; 13 | const maxPollAttempts = 100000; 14 | const pollTimeout = 10; 15 | -------------------------------------------------------------------------------- /test-html/sharedMemCriticalSection_master.js: -------------------------------------------------------------------------------- 1 | // Testing mutual exclusion on SharedArrayBuffer - master worker. 2 | // 3 | // A shared buffer is allocated and shared between three workers. 4 | // Each worker treats the buffer as an int32 array, each element is 5 | // treated as three 10-bit fields. Each time a worker enters the 6 | // critical section it increments its bitfield within each element. 7 | // 8 | // Each time the worker leaves the critical section it attempts to 9 | // yield the scheduler. 10 | // 11 | // At the end, all bit fields of all elements should have the number 12 | // of iterations executed. (Otherwise, mutual exclusion was not 13 | // achieved.) 14 | // 15 | // We can observe failures by disabling locking, set doLock=false in 16 | // sharedMemCriticalSection_defs.js. 17 | 18 | importScripts("worker-harness.js", 19 | "sharedMemCriticalSection_defs.js"); 20 | 21 | var nbytes = 4*(arrayElements + arrayExtra) + 4095 & ~4095; 22 | var sab = new SharedArrayBuffer(nbytes); 23 | 24 | var sia = new Int32Array(sab); 25 | sia[mutexLoc] = 0; 26 | sia[statusLoc] = 0; 27 | for ( var x=arrayLo ; x < arrayLim ; x++ ) 28 | sia[x] = 0; 29 | 30 | onmessage = function (ev) { 31 | if (!ev.data) 32 | return; 33 | 34 | postMessage("Creating workers"); 35 | var ws = []; 36 | for ( var x=0 ; x < workers ; x++ ) { 37 | var w = new Worker("sharedMemCriticalSection_slave.js"); 38 | w.onmessage = 39 | function (ev) { 40 | self.postMessage(ev.data); 41 | }; 42 | ws.push(w); 43 | } 44 | 45 | postMessage("Starting workers"); 46 | for ( var w in ws ) 47 | ws[w].postMessage({id: w, sab: sab}, [sab]); 48 | 49 | setTimeout(waitUntilDone, 0); 50 | } 51 | 52 | var attempts = 0; 53 | var laststat = -1; 54 | function waitUntilDone() { 55 | var c = Atomics.load(sia, statusLoc); 56 | if (c != laststat) { 57 | postMessage("Master status = " + c); 58 | laststat = c; 59 | } 60 | if (c != (1 << workers)-1) { 61 | if (attempts++ > maxPollAttempts) { 62 | postMessage("Giving up - takes too long!"); 63 | return; 64 | } 65 | setTimeout(waitUntilDone, pollTimeout); 66 | return; 67 | } 68 | 69 | var correct = (iterations << 20) | (iterations << 10) | iterations; 70 | var bogus = 0; 71 | var numfail = 0; 72 | for ( var x=arrayLo ; x < arrayLim ; x++ ) 73 | if (sia[x] != correct) { 74 | if (++bogus < 20) 75 | postMessage("Incorrect @ " + x + ": expected " + correct + "; got " + sia[x]); 76 | numfail++; 77 | } 78 | 79 | if (numfail) 80 | postMessage("FAILURE Number of failures = " + numfail); 81 | else { 82 | postMessage("SUCCESS"); 83 | var n = sia[arrayLo]; 84 | for ( var i=0 ; i < workers ; i++ ) { 85 | postMessage("Worker " + i + ": " + (n & 1023).toString()); 86 | n >>= 10; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test-html/sharedMemCriticalSection_slave.js: -------------------------------------------------------------------------------- 1 | // Testing mutual exclusion on SharedArrayBuffer - slave worker. 2 | // 3 | // Worker code: 4 | // 5 | // Each array element comprises 3 10-bit fields. The final value of 6 | // each element should be (iter << 20) | (iter << 10) | iter, if 7 | // mutual exclusion works OK. 8 | // 9 | // To signal completion this one or's a bit into a bit vector at a 10 | // known location. This is crude but it works. There are other ways. 11 | 12 | importScripts("worker-harness.js", 13 | "sharedMemCriticalSection_defs.js", 14 | "sharedMemSimpleMutex.js"); 15 | 16 | var iter = 0; // Global iteration counter 17 | 18 | var id; // Worker identity (0..workers-1) 19 | var sia; // Shared Int32Array 20 | var m; // Mutex 21 | 22 | function compute() { 23 | if (iter == iterations) { 24 | postMessage("Finished: " + id); 25 | Atomics.or(sia, statusLoc, 1 << id); 26 | return; 27 | } 28 | 29 | iter++; 30 | if (doLock) 31 | m.lock(); 32 | for ( var x=arrayLo ; x < arrayLim ; x++ ) 33 | sia[x] += (1 << id*10); 34 | if (doLock) 35 | m.unlock(); 36 | setTimeout(compute, 1); // relax 37 | } 38 | 39 | onmessage = function (ev) { 40 | if (!ev.data) 41 | return; 42 | 43 | var msg = ev.data; 44 | id = msg.id; 45 | sia = new Int32Array(msg.sab); 46 | m = new Mutex(sia, mutexLoc); 47 | postMessage("Starting: " + id); 48 | setTimeout(compute, 0); 49 | } 50 | -------------------------------------------------------------------------------- /test-html/sharedMemSimpleMutex.js: -------------------------------------------------------------------------------- 1 | // Simple mutex semaphore abstraction. 2 | 3 | // The mutex code is based on http://www.akkadia.org/drepper/futex.pdf 4 | // 5 | // Mutex state values: 6 | // 0: unlocked 7 | // 1: locked with no waiters 8 | // 2: locked with possible waiters 9 | 10 | // "sab" must be a shared Int32Array (mapped onto a SharedArrayBuffer). 11 | // "index" must be a valid index in sab, reserved for the mutex. 12 | // sab[index] must be initialized (globally) to 0 before the first mutex is created. 13 | 14 | function Mutex(sab, index) { 15 | this.sab = sab; 16 | this.index = index; 17 | } 18 | 19 | Mutex.prototype.lock = 20 | function () { 21 | const sab = this.sab; 22 | const index = this.index; 23 | var c; 24 | if ((c = Atomics.compareExchange(sab, index, 0, 1)) != 0) { 25 | do { 26 | if (c == 2 || Atomics.compareExchange(sab, index, 1, 2) != 0) 27 | Atomics.wait(sab, index, 2, Number.POSITIVE_INFINITY); 28 | } while ((c = Atomics.compareExchange(sab, index, 0, 2)) != 0); 29 | } 30 | }; 31 | 32 | Mutex.prototype.unlock = 33 | function () { 34 | const sab = this.sab; 35 | const index = this.index; 36 | var v0 = Atomics.sub(sab, index, 1); 37 | // Wake up a waiter if there are any 38 | if (v0 != 1) { 39 | Atomics.store(sab, index, 0); 40 | Atomics.wake(sab, index, 1); 41 | } 42 | }; 43 | -------------------------------------------------------------------------------- /test-html/sharedTypedArrayClone-worker.js: -------------------------------------------------------------------------------- 1 | importScripts("worker-harness.js"); 2 | 3 | var state = 0; 4 | var sab; 5 | 6 | onmessage = function (ev) { 7 | if (ev.data === "start") { 8 | postMessage("ready"); 9 | return; 10 | } 11 | switch (state) { 12 | case 0: 13 | if (!(ev.data instanceof SharedArrayBuffer)) 14 | postMessage("ERROR: 0: Not SharedArrayBuffer"); 15 | if (ev.data.byteLength != 4) 16 | postMessage("ERROR: 0: Length wrong: " + ev.data.byteLength); 17 | postMessage("0: OK"); 18 | sab = ev.data; // This will be the same memory we receive next 19 | state++; 20 | break; 21 | case 1: 22 | if (!(ev.data instanceof Int32Array)) 23 | postMessage("ERROR: 1: Not shared Int32Array"); 24 | if (ev.data.length != 1) 25 | postMessage("ERROR: 1: Length wrong: " + ev.data.length); 26 | ev.data[0] = 1337; 27 | if ((new Int32Array(sab))[0] != 1337) 28 | postMessage("ERROR: 1: Not the same memory"); 29 | postMessage("1: OK"); 30 | state++; 31 | break; 32 | default: 33 | postMessage("Unknown state: " + state); 34 | break; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test-html/sharing_sharedworker_and_nested_worker_child.js: -------------------------------------------------------------------------------- 1 | // A shared buffer is allocated in the worker, sent to the child, and 2 | // sent back to the worker, which tests that it got the right buffer. 3 | 4 | importScripts("worker-harness.js"); 5 | 6 | onmessage = function (ev) { 7 | let newsab = ev.data; 8 | if (!(newsab instanceof SharedArrayBuffer)) { 9 | postMessage("Bad type in child"); 10 | return; 11 | } 12 | if (newsab.byteLength != 4096) { 13 | postMessage("Bad length in child"); 14 | return; 15 | } 16 | postMessage(newsab); 17 | } 18 | -------------------------------------------------------------------------------- /test-html/sharing_sharedworker_and_nested_worker_parent.js: -------------------------------------------------------------------------------- 1 | // A shared buffer is allocated in the worker, sent to the nested worker, and 2 | // sent back to the worker, which tests that it got the right buffer. 3 | 4 | importScripts("worker-harness.js"); 5 | 6 | var sab = new SharedArrayBuffer(4096); // Don't change this length, the child knows about it 7 | var nested_worker = new Worker("sharing_sharedworker_and_nested_worker_child.js"); 8 | 9 | var port; 10 | 11 | nested_worker.onmessage = function (ev) { 12 | if (typeof ev.data == "string") { 13 | port.postMessage(ev.data); 14 | return; 15 | } 16 | let newsab = ev.data; 17 | if (!(newsab instanceof SharedArrayBuffer)) { 18 | port.postMessage("Bad type"); 19 | return; 20 | } 21 | if (newsab.byteLength != sab.byteLength) { 22 | port.postMessage("Bad length"); 23 | return; 24 | } 25 | var t0 = new Int32Array(sab); 26 | var t1 = new Int32Array(newsab); 27 | t1[0] = 0xcafebabe; 28 | t0[0] = 0; 29 | if (t1[0] != 0) { 30 | port.postMessage("Not the same memory"); 31 | return; 32 | } 33 | port.postMessage("SUCCESS"); 34 | } 35 | 36 | onconnect = function (ev) { 37 | port = ev.ports[0]; 38 | port.postMessage("It's alive!"); 39 | port.onmessage = function (ev) { 40 | if (ev.data === "START") { 41 | port.postMessage("Starting"); 42 | nested_worker.postMessage(sab, [sab]); 43 | return; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test-html/sharing_window_and_sharedworker_master.js: -------------------------------------------------------------------------------- 1 | if (!this.SharedWorker) 2 | msg("FAILURE: SharedWorker not available"); 3 | else if (!this.SharedArrayBuffer) 4 | msg("FAILURE: Shared memory not available"); 5 | else 6 | runTest(); 7 | 8 | function runTest() { 9 | var w = new SharedWorker("sharing_window_and_sharedworker_worker.js"); 10 | var sab = new SharedArrayBuffer(65536); 11 | 12 | w.port.onmessage = function (ev) { 13 | if (typeof ev.data == "object" && ev.data instanceof SharedArrayBuffer) { 14 | msg("SHOULD NOT HAPPEN: Shared memory received in master\n" + 15 | " datum=" + ev.data.constructor + 16 | " length=" + ev.data.byteLength); 17 | } 18 | else 19 | msg("" + ev.data); 20 | } 21 | 22 | // The following message should not be received by the worker because 23 | // it tries to transmit shared memory. 24 | 25 | try { 26 | w.port.postMessage(sab, [sab]); 27 | } 28 | catch (e) { 29 | msg("SUCCESS - Failed to send in master"); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test-html/sharing_window_and_sharedworker_worker.js: -------------------------------------------------------------------------------- 1 | // The SharedArrayBuffer is available in the SharedWorker, and shared 2 | // memory can be created, it just can't be shared. This makes 3 | // slightly more sense than making the APIs unavailable because it 4 | // allows for code reuse in the absence of active sharing. 5 | 6 | onconnect = function (ev) { 7 | var port = ev.ports[0]; 8 | port.onmessage = function (ev) { 9 | if (ev.data instanceof SharedArrayBuffer) 10 | port.postMessage("SHOULD NOT HAPPEN: Shared memory received in worker\n" + 11 | " datum=" + ev.data.constructor + "\n" + 12 | " length=" + ev.data.byteLength); 13 | else 14 | port.postMessage("Random data received in worker: " + ev.data); 15 | }; 16 | 17 | var sab = new SharedArrayBuffer(100); 18 | 19 | // The following message should not be received by the master because it 20 | // tries to transmit shared memory. 21 | 22 | setTimeout(function () { 23 | try { 24 | port.postMessage(sab, [sab]); 25 | } catch (e) { 26 | port.postMessage("SUCCESS - Failed to send in worker"); 27 | } 28 | }, 1000); 29 | } 30 | -------------------------------------------------------------------------------- /test-html/sharing_window_and_worker.js: -------------------------------------------------------------------------------- 1 | // A shared buffer is allocated in the worker, sent to the master, and 2 | // sent back to the worker, which tests that it got the right buffer. 3 | 4 | importScripts("worker-harness.js"); 5 | 6 | var sab = new SharedArrayBuffer(4096); 7 | 8 | onmessage = function (ev) { 9 | if (ev.data === "START") { 10 | postMessage(sab, [sab]); 11 | return; 12 | } 13 | 14 | let newsab = ev.data; 15 | if (!(newsab instanceof SharedArrayBuffer)) { 16 | postMessage("Bad type"); 17 | return; 18 | } 19 | if (newsab.byteLength != sab.byteLength) { 20 | postMessage("Bad length"); 21 | return; 22 | } 23 | var t0 = new Int32Array(sab); 24 | var t1 = new Int32Array(newsab); 25 | t1[0] = 0xcafebabe; 26 | t0[0] = 0; 27 | if (t1[0] != 0) { 28 | postMessage("Not the same memory"); 29 | return; 30 | } 31 | postMessage("SUCCESS"); 32 | } 33 | -------------------------------------------------------------------------------- /test-html/sharing_worker_and_nested_worker_child.js: -------------------------------------------------------------------------------- 1 | // A shared buffer is allocated in the worker, sent to the child, and 2 | // sent back to the worker, which tests that it got the right buffer. 3 | 4 | importScripts("worker-harness.js"); 5 | 6 | onmessage = function (ev) { 7 | let newsab = ev.data; 8 | if (!(newsab instanceof SharedArrayBuffer)) { 9 | postMessage("Bad type in child"); 10 | return; 11 | } 12 | if (newsab.byteLength != 4096) { 13 | postMessage("Bad length in child"); 14 | return; 15 | } 16 | postMessage(newsab); 17 | } 18 | -------------------------------------------------------------------------------- /test-html/sharing_worker_and_nested_worker_parent.js: -------------------------------------------------------------------------------- 1 | // A shared buffer is allocated in the worker, sent to the nested worker, and 2 | // sent back to the worker, which tests that it got the right buffer. 3 | 4 | importScripts("worker-harness.js"); 5 | 6 | var sab = new SharedArrayBuffer(4096); // Don't change this length, the child knows about it 7 | var nested_worker = new Worker("sharing_worker_and_nested_worker_child.js"); 8 | 9 | nested_worker.onmessage = function (ev) { 10 | let newsab = ev.data; 11 | if (!(newsab instanceof SharedArrayBuffer)) { 12 | postMessage("Bad type"); 13 | return; 14 | } 15 | if (newsab.byteLength != sab.byteLength) { 16 | postMessage("Bad length"); 17 | return; 18 | } 19 | var t0 = new Int32Array(sab); 20 | var t1 = new Int32Array(newsab); 21 | t1[0] = 0xcafebabe; 22 | t0[0] = 0; 23 | if (t1[0] != 0) { 24 | postMessage("Not the same memory"); 25 | return; 26 | } 27 | postMessage("SUCCESS"); 28 | } 29 | 30 | onmessage = function (ev) { 31 | if (ev.data === "START") { 32 | nested_worker.postMessage(sab, [sab]); 33 | return; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /test-html/test_futexInterrupt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests that futexes can be interrupted on tab close or browser shutdown 6 | 7 | 8 | 9 | 10 |

This tests that futexes that are blocked will be interrupted and will 11 | not block the browser when the tab is closed or the browser is shut 12 | down.

13 | 14 |

Open this page in the browser. It will create a worker that 15 | blocks indefinitely in Atomics.wait(). No line should appear below that 16 | says "THIS SHOULD NOT HAPPEN".

17 | 18 | 19 | 20 |

Now try to close the tab or close the browser! If the code works 21 | as it should, the tab or browser should close; if you close the 22 | browser you should see no trace of the browser process in a process 23 | monitor or similar.

24 | 25 |

(Firefox devs: try ./mach run from the command prompt, ideally in a debug build, 26 | since release builds will tend to just exit in a hard way if things get stuck.)

27 | 28 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test-html/test_futexMainTimeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests the timeout of Atomics.wait on the main thread 6 | 7 | 8 | 9 | 10 |

Tests Atomics.wait timeouts on the main thread.

11 | 12 |

Just load the page, click , and leave the browser alone. The main thread will go 14 | to sleep for a few seconds and then time out, you should see some 15 | output below. If you receive the slow-script dialog something is 16 | wrong. 17 | 18 |

Also check the console for errors. 19 | 20 | 21 | 22 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /test-html/test_futexMainWakeup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests that the main thread can be awoken by a worker 6 | 7 | 8 | 9 | 10 |

Tests that the main thread can be awoken by a worker.

11 | 12 |

Just load the page, click , and leave the browser alone. The main thread will go 14 | to sleep for a few seconds and then be worken by a worker thread, you 15 | should see some output below. If you receive the slow-script dialog 16 | something is wrong. 17 | 18 |

Also check the console for errors. 19 | 20 | 21 | 22 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /test-html/test_futexSlowscript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests the slow script dialog within a Atomics.wait on the main thread 6 | 7 | 8 | 9 | 10 |

Tests the slow script dialog within a Atomics.wait on the main thread. This may be Firefox-specific at the moment.

11 | 12 |

Just load the page, click , and leave the browser alone. The slow script dialog 14 | should appear after a while. You should be able to Continue the 15 | script without anything happening except the dialog appearing again 16 | later. You should be able to Stop the script and have the browser go 17 | back to being its responsive self, without any scary-looking output 18 | appearing below (there will be some output). 19 | 20 |

Remember to beween 21 | each test, when the script is killed 22 | it leaves global status variables in an inconsistent state. 23 | 24 |

Also check the console, once you kill the script there should be a 25 | stack trace there. 26 | 27 |

Try these experiments: 28 |

33 | 34 |

Additionally you should be able to bring up the script in a debugger by 35 | clicking "Debug script" in the slow-script dialog. (On my system this causes 36 | one of my cores to stay at 100% utilization.) 37 | 38 | 39 | 40 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /test-html/test_futexTimeout.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | wait, wake, and timeout 6 | 7 | 8 | 9 | 10 |

Tests Atomics.wait, Atomics.wake, and timeout functionality.

11 | 12 |

The tests run when the page is loaded. The output from these 13 | tests will appear below. If everything goes OK it should end with the 14 | line DONE after a while, and there should be no lines that start with 15 | FAIL.

16 | 17 |

Also check the console for errors; there should be none.

18 | 19 | 20 | 21 | 225 | 226 | 227 | 228 | -------------------------------------------------------------------------------- /test-html/test_futexWakeInInterrupt.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests wakeup on Atomics.wait stuck in an interrupt callback 6 | 7 | 8 | 9 | 10 |

Firefox note: This test does not work with e10s (process separation) enabled. 11 | 12 |

Just load the page, click , and leave the browser alone. The main thread will go 14 | to sleep for 10-15 seconds and then time out, and you'll see the slow-script dialog. 15 | 16 |

No earlier than 25 seconds after clicking the button originally, 17 | hit the Continue button on the slow-script dialog. The timing is important; don't 18 | click it too early. 19 | 20 |

Immediately after the dialog closes you should see output in the page below. 21 | If you get a second slow-script dialog then something has probably gone wrong. 22 | 23 |

Also check the console for errors. 24 | 25 | 26 | 27 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /test-html/test_serviceWorkerNotSharing.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tests that a ServiceWorker can't send or receive shared memory 8 | 9 | 10 | 11 |

Tests that shared memory cannot be sent to or from a 12 | ServiceWorker instance.

13 | 14 |

This test needs to access the web (it uses HTTPS). It will load 15 | files from 16 | https://lars-t-hansen.github.io/ecmascript_sharedmem/tests/.

17 | 18 |

Click 19 | here to load and run the test.

20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test-html/test_sharedMemCriticalSection.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests of mutual exclusion on SharedArrayBuffer using Atomics 6 | 7 | 8 | 9 |

This test case tests that mutual exclusion on SharedArrayBuffer 10 | using Atomics operations works.

11 | 12 |

The test runs when you load the page. It usually takes 1-5 13 | seconds and the output appears below. There should be no error 14 | messages and finally a line that says SUCCESS (there may be even more 15 | output after that). Also check the browser console for errors, there should 16 | be none.

17 | 18 | 19 | 20 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /test-html/test_sharedTypedArrayClone.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests structured clone of SharedInt32Array and SharedArrayBuffer 6 | 7 | 8 | 9 | 10 |

This tests that SharedArrayBuffer and SharedInt32Array values can be 11 | transmitted to a worker.

12 | 13 |

The test runs when you load the page. Look for "DONE" below this 14 | paragraph. There will be other lines but there should be no error 15 | messages in the output or in the browser console.

16 | 17 | 18 | 19 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /test-html/test_sharing_sharedworker_and_nested_worker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests sharing memory between a SharedWorker and its nested Worker 6 | 7 | 8 | 9 | 10 |

This test case tests that sharing memory by postMessage between a 11 | SharedWorker and its nested Worker works in both directions.

12 | 13 |

The test runs when you load the page. It usually takes little 14 | time and the output appears below. There should be no error messages 15 | and finally a line that says SUCCESS (there may be even more output 16 | after that). Also check the browser console for errors, there should 17 | be none.

18 | 19 | 20 | 21 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test-html/test_sharing_window_and_sharedworker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests sharing memory between a Window and its SharedWorker 6 | 7 | 8 | 9 |

This test case tests that sharing memory by postMessage between a 10 | Window and its SharedWorker does not work in either direction.

11 | 12 |

If everything goes OK there should be no lines below saying 13 | "SHOULD NOT HAPPEN", and instead there should be lines saying 14 | "SUCCESS - Failed to send".

15 | 16 |

Also check the console, there should be nothing unusual there.

17 | 18 |
19 |   
20 | 
21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /test-html/test_sharing_window_and_worker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests sharing memory between a Window and its Worker 6 | 7 | 8 | 9 |

This test case tests that sharing memory by postMessage between a 10 | Window and its Worker works in both directions.

11 | 12 |

The test runs when you load the page. It usually takes little 13 | time and the output appears below. There should be no error messages 14 | and finally a line that says SUCCESS (there may be even more output 15 | after that). Also check the browser console for errors, there should 16 | be none.

17 | 18 | 19 | 20 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /test-html/test_sharing_worker_and_nested_worker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Tests sharing memory between a Worker and its nested Worker 6 | 7 | 8 | 9 | 10 |

This test case tests that sharing memory by postMessage between a 11 | Worker and its nested Worker works in both directions.

12 | 13 |

The test runs when you load the page. It usually takes little 14 | time and the output appears below. There should be no error messages 15 | and finally a line that says SUCCESS (there may be even more output 16 | after that). Also check the browser console for errors, there should 17 | be none.

18 | 19 | 20 | 21 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /test-html/worker-harness.js: -------------------------------------------------------------------------------- 1 | /* Empty, for now */ 2 | -------------------------------------------------------------------------------- /test262/LICENSE.md: -------------------------------------------------------------------------------- 1 | The intended license for the test262/ subdirectory of the 2 | ecmacript_sharedmem repository is the BSD license. 3 | -------------------------------------------------------------------------------- /test262/README.md: -------------------------------------------------------------------------------- 1 | These are test cases that should be largely compatible with the 2 | test262 framework, only minor editing should be necessary to 3 | incorporate them into the test262 test suite later. 4 | 5 | At the moment, these test cases use their own test runner and harness 6 | however, and each incorporates a little prologue and epilogue that 7 | will later be removed. 8 | 9 | In a browser, load runner.html to run all the tests. 10 | 11 | In a JS shell, run each test case by first loading harness.js and then 12 | the test case. 13 | -------------------------------------------------------------------------------- /test262/agenttest_spidermonkey.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | // 4 | 5 | // AgentTest infrastructure for JS shells. Load this as a prefix to 6 | // tests that need to run in multiple agents. 7 | // 8 | // (This implementation for the SpiderMonkey shell.) 9 | // 10 | // 11 | // In the main thread, there is an object AgentTest: 12 | // 13 | // AgentTest.start(script_source) => void 14 | // Run the `script_source` program in a concurrent agent. 15 | // Blocks until that agent is up and running. 16 | // 17 | // AgentTest.broadcast(id, sab) => void 18 | // Send the pair {id, sab} to all concurrent agents. `id` will be 19 | // coerced to Int32. `sab` must be a SharedArrayBuffer. Blocks 20 | // until all the agents have acknowedged receipt of the message. 21 | // 22 | // Note, broadcast assumes all agents that were started are still 23 | // alive and will deadlock if any have terminated. 24 | // 25 | // AgentTest.getReport() => string | null 26 | // Probe for a text message from any agent, return null if none is 27 | // available. Will not block. 28 | // 29 | // AgentTest.sleep(ms) 30 | // Wait for `ms` milliseconds. 31 | // 32 | // AgentTest.reset() 33 | // All concurrent agents should have exited - this will be assumed, 34 | // not checked! Reinitialize global state for a new round of tests. 35 | // 36 | // 37 | // In the concurrent agent, there is also an object AgentTest: 38 | // 39 | // AgentTest.receive() 40 | // Block until a {sab, id} pair is broadcast from the master, then 41 | // return it. 42 | // 43 | // AgentTest.report(string) 44 | // Report the string back to the master, will not block. Messages 45 | // should be short and sweet, as there is fixed, limited space. 46 | // 47 | // AgentTest.sleep(ms) 48 | // Wait for `ms` milliseconds. 49 | // 50 | // AgentTest.leaving() 51 | // The agent is about to terminate. (This will not be called if the 52 | // agent crashes and is here as a portable hook to make the agent 53 | // terminate itself, should it need to.) 54 | 55 | // ------------------------------------------------------------ 56 | 57 | // The SpiderMonkey implementation uses a designated shared buffer _ia 58 | // for coordination, and spinlocks for everything except sleeping. 59 | 60 | var _MSG_LOC = 0; // Low bit set: broadcast available; High bits: seq # 61 | var _ID_LOC = 1; // ID sent with broadcast 62 | var _ACK_LOC = 2; // Worker increments this to ack that broadcast was received 63 | var _RDY_LOC = 3; // Worker increments this to ack that worker is up and running 64 | var _LOCKTXT_LOC = 4; // Writer lock for the text buffer: 0=open, 1=closed 65 | var _NUMTXT_LOC = 5; // Count of messages in text buffer 66 | var _NEXT_LOC = 6; // First free location in the buffer 67 | var _SLEEP_LOC = 7; // Used for sleeping 68 | 69 | var _FIRST = 10; // First location of first message 70 | 71 | var _ia = new Int32Array(new SharedArrayBuffer(65536)); 72 | _ia[_NEXT_LOC] = _FIRST; 73 | 74 | // AgentTest object in the agent, prepended to the agent's source. 75 | 76 | var _worker_AgentTest = ` 77 | var AgentTest = 78 | { 79 | receive: function() { 80 | var k; 81 | while (((k = Atomics.load(_ia, ${_MSG_LOC})) & 1) == 0) 82 | ; 83 | var received_sab = getSharedArrayBuffer(); 84 | var received_id = Atomics.load(_ia, ${_ID_LOC}); 85 | Atomics.add(_ia, ${_ACK_LOC}, 1); 86 | while (Atomics.load(_ia, ${_MSG_LOC}) == k) 87 | ; 88 | return { id: received_id, sab: received_sab }; 89 | }, 90 | 91 | report: function(msg) { 92 | while (Atomics.compareExchange(_ia, ${_LOCKTXT_LOC}, 0, 1) == 1) 93 | ; 94 | msg = "" + msg; 95 | var i = _ia[${_NEXT_LOC}]; 96 | _ia[i++] = msg.length; 97 | for ( let j=0 ; j < msg.length ; j++ ) 98 | _ia[i++] = msg.charCodeAt(j); 99 | _ia[${_NEXT_LOC}] = i; 100 | Atomics.add(_ia, ${_NUMTXT_LOC}, 1); 101 | Atomics.store(_ia, ${_LOCKTXT_LOC}, 0); 102 | }, 103 | 104 | sleep: function(s) { 105 | Atomics.wait(_ia, ${_SLEEP_LOC}, 0, s); 106 | }, 107 | 108 | leaving: function() {} 109 | } 110 | var _ia = new Int32Array(getSharedArrayBuffer()); 111 | Atomics.add(_ia, ${_RDY_LOC}, 1); 112 | `; 113 | 114 | // AgentTest object in the main thread. 115 | 116 | var AgentTest = 117 | { 118 | _numWorkers: 0, 119 | _numReports: 0, 120 | _reportPtr: _FIRST, 121 | 122 | start: function (script) { 123 | setSharedArrayBuffer(_ia.buffer); 124 | var oldrdy = Atomics.load(_ia, _RDY_LOC); 125 | evalInWorker(_worker_AgentTest + script); 126 | while (Atomics.load(_ia, _RDY_LOC) == oldrdy) 127 | ; 128 | this._numWorkers++; 129 | }, 130 | 131 | broadcast: function (sab, id) { 132 | setSharedArrayBuffer(sab); 133 | Atomics.store(_ia, _ID_LOC, id); 134 | Atomics.store(_ia, _ACK_LOC, 0); 135 | Atomics.add(_ia, _MSG_LOC, 1); 136 | while (Atomics.load(_ia, _ACK_LOC) < this._numWorkers) 137 | ; 138 | Atomics.add(_ia, _MSG_LOC, 1); 139 | }, 140 | 141 | getReport: function () { 142 | if (this._numReports == Atomics.load(_ia, _NUMTXT_LOC)) 143 | return null; 144 | var s = ""; 145 | var i = this._reportPtr; 146 | var len = _ia[i++]; 147 | for ( let j=0 ; j < len ; j++ ) 148 | s += String.fromCharCode(_ia[i++]); 149 | this._reportPtr = i; 150 | this._numReports++; 151 | return s; 152 | }, 153 | 154 | sleep: function(s) { 155 | Atomics.wait(_ia, _SLEEP_LOC, 0, s); 156 | }, 157 | 158 | reset: function() { 159 | this._numWorkers = 0; 160 | this._numReports = 0; 161 | this._reportPtr = _FIRST; 162 | _ia[_MSG_LOC] = 0; 163 | _ia[_ID_LOC] = 0; 164 | _ia[_ACK_LOC] = 0; 165 | _ia[_RDY_LOC] = 0; 166 | _ia[_LOCKTXT_LOC] = 0; 167 | _ia[_NUMTXT_LOC] = 0; 168 | _ia[_NEXT_LOC] = _FIRST; 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /test262/atomics-on-good-arrays.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("atomics-on-good-arrays"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test atomic operations on arrays that allow atomic operations 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | var ab = new ArrayBuffer(16); 16 | 17 | var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; 18 | 19 | for ( var idx=0 ; idx < int_views.length ; idx++ ) { 20 | var View = int_views[idx]; 21 | 22 | // Make it interesting - use non-zero byteOffsets and non-zero indexes 23 | 24 | var view = new View(sab, 32, 20); 25 | 26 | assert.sameValue(Atomics.store(view, 3, 10), 10); 27 | assert.sameValue(Atomics.load(view, 3), 10); 28 | assert.sameValue(Atomics.exchange(view, 3, 5), 10); 29 | assert.sameValue(Atomics.load(view, 3), 5); 30 | assert.sameValue(Atomics.compareExchange(view, 3, 5, 8), 5); 31 | assert.sameValue(Atomics.load(view, 3), 8); 32 | assert.sameValue(Atomics.compareExchange(view, 3, 5, 16), 8); 33 | assert.sameValue(Atomics.load(view, 3), 8); 34 | 35 | assert.sameValue(Atomics.add(view, 8, 10), 0); // 0 + 10 -> 10 36 | assert.sameValue(Atomics.sub(view, 8, 5), 10); // 10 - 5 -> 5 37 | assert.sameValue(Atomics.and(view, 8, 3), 5); // 5 & 3 -> 1 38 | assert.sameValue(Atomics.or(view, 8, 6), 1); // 1 | 6 -> 7 39 | assert.sameValue(Atomics.xor(view, 8, 2), 7); // 7 ^ 2 -> 5 40 | assert.sameValue(Atomics.load(view, 8), 5); // 5 41 | 42 | // Rudimentary tests for sign extension and chopping. 43 | 44 | var control = new View(ab, 0, 2); 45 | var r; 46 | 47 | r = Atomics.store(view, 3, -5); control[0] = -5; 48 | assert.sameValue(r, -5); 49 | assert.sameValue(Atomics.load(view, 3), control[0]); 50 | assert.sameValue(Atomics.exchange(view, 3, 0), control[0]); 51 | view[3] = -5; 52 | assert.sameValue(Atomics.add(view, 3, 0), control[0]); 53 | assert.sameValue(Atomics.sub(view, 3, 0), control[0]); 54 | assert.sameValue(Atomics.and(view, 3, -1), control[0]); 55 | assert.sameValue(Atomics.or(view, 3, 0), control[0]); 56 | assert.sameValue(Atomics.xor(view, 3, 0), control[0]); 57 | control[1] = -7; 58 | assert.sameValue(Atomics.compareExchange(view, 3, control[0], -7), control[0]); 59 | assert.sameValue(Atomics.compareExchange(view, 3, control[0], -9), control[1]); 60 | assert.sameValue(view[3], control[1]); 61 | 62 | r = Atomics.store(view, 3, 12345); control[0] = 12345; 63 | assert.sameValue(r, 12345); 64 | assert.sameValue(Atomics.load(view, 3), control[0]); 65 | assert.sameValue(Atomics.exchange(view, 3, 0), control[0]); 66 | view[3] = 12345; 67 | assert.sameValue(Atomics.add(view, 3, 0), control[0]); 68 | assert.sameValue(Atomics.sub(view, 3, 0), control[0]); 69 | assert.sameValue(Atomics.and(view, 3, -1), control[0]); 70 | assert.sameValue(Atomics.or(view, 3, 0), control[0]); 71 | assert.sameValue(Atomics.xor(view, 3, 0), control[0]); 72 | control[1] = 23456; 73 | assert.sameValue(Atomics.compareExchange(view, 3, control[0], 23456), control[0]); 74 | assert.sameValue(Atomics.compareExchange(view, 3, control[0], 34567), control[1]); 75 | assert.sameValue(view[3], control[1]); 76 | 77 | r = Atomics.store(view, 3, 123456789); control[0] = 123456789; 78 | assert.sameValue(r, 123456789); 79 | assert.sameValue(Atomics.load(view, 3), control[0]); 80 | assert.sameValue(Atomics.exchange(view, 3, 0), control[0]); 81 | view[3] = 123456789; 82 | assert.sameValue(Atomics.add(view, 3, 0), control[0]); 83 | assert.sameValue(Atomics.sub(view, 3, 0), control[0]); 84 | assert.sameValue(Atomics.and(view, 3, -1), control[0]); 85 | assert.sameValue(Atomics.or(view, 3, 0), control[0]); 86 | assert.sameValue(Atomics.xor(view, 3, 0), control[0]); 87 | control[1] = 234567890; 88 | assert.sameValue(Atomics.compareExchange(view, 3, control[0], 234567890), control[0]); 89 | assert.sameValue(Atomics.compareExchange(view, 3, control[0], 345678901), control[1]); 90 | assert.sameValue(view[3], control[1]); 91 | 92 | r = Atomics.store(view, 3, Math.PI); control[0] = Math.PI; 93 | assert.sameValue(r, Math.PI); 94 | assert.sameValue(Atomics.load(view, 3), control[0]); 95 | assert.sameValue(Atomics.exchange(view, 3, 0), control[0]); 96 | 97 | // Do not affect memory outside the elements written. 98 | 99 | for ( var i=0 ; i < view.length ; i++ ) { 100 | if (i == 3 || i == 8) 101 | continue; 102 | assert.sameValue(view[i], 0); 103 | } 104 | 105 | view[3] = 0; 106 | view[8] = 0; 107 | } 108 | 109 | // BEGIN EPILOGUE 110 | finishTest("atomics-on-good-arrays"); 111 | // END EPILOGUE 112 | -------------------------------------------------------------------------------- /test262/atomics-on-nonshared-int-arrays.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("atomics-on-nonshared-int-arrays"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test atomic operations on non-shared integer TypedArrays 12 | ---*/ 13 | 14 | var ab = new ArrayBuffer(16); 15 | 16 | var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; 17 | 18 | for ( var View of int_views ) { 19 | var view = new View(ab); 20 | 21 | assert.throws(TypeError, (() => Atomics.load(view, 0))); 22 | assert.throws(TypeError, (() => Atomics.store(view, 0, 0))); 23 | assert.throws(TypeError, (() => Atomics.exchange(view, 0, 0))); 24 | assert.throws(TypeError, (() => Atomics.compareExchange(view, 0, 0, 0))); 25 | assert.throws(TypeError, (() => Atomics.add(view, 0, 0))); 26 | assert.throws(TypeError, (() => Atomics.sub(view, 0, 0))); 27 | assert.throws(TypeError, (() => Atomics.and(view, 0, 0))); 28 | assert.throws(TypeError, (() => Atomics.or(view, 0, 0))); 29 | assert.throws(TypeError, (() => Atomics.xor(view, 0, 0))); 30 | } 31 | 32 | // BEGIN EPILOGUE 33 | finishTest("atomics-on-nonshared-int-arrays"); 34 | // END EPILOGUE 35 | -------------------------------------------------------------------------------- /test262/atomics-on-other-stuff.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("atomics-on-other-stuff"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test atomic operations on values other than TypedArrays 12 | ---*/ 13 | 14 | var values = [null, 15 | undefined, 16 | true, 17 | false, 18 | new Boolean(true), 19 | 10, 20 | 3.14, 21 | new Number(4), 22 | "Hi there", 23 | new Date, 24 | /a*utomaton/g, 25 | { password: "qumquat" }, 26 | new DataView(new ArrayBuffer(10)), 27 | new ArrayBuffer(128), 28 | new SharedArrayBuffer(128), 29 | new Error("Ouch"), 30 | [1,1,2,3,5,8], 31 | ((x) => -x), 32 | new Map(), 33 | new Set(), 34 | new WeakMap(), 35 | new WeakSet(), 36 | this.Promise ? new Promise(() => "done") : undefined, 37 | Symbol("halleluja"), 38 | // TODO: Proxy? 39 | Object, 40 | Int32Array, 41 | Date, 42 | Math, 43 | Atomics ]; 44 | 45 | for ( var i=0 ; i < values.length ; i++ ) { 46 | var view = values[i]; 47 | assert.throws(TypeError, (() => Atomics.load(view, 0))); 48 | assert.throws(TypeError, (() => Atomics.store(view, 0, 0))); 49 | assert.throws(TypeError, (() => Atomics.exchange(view, 0, 0))); 50 | assert.throws(TypeError, (() => Atomics.compareExchange(view, 0, 0, 0))); 51 | assert.throws(TypeError, (() => Atomics.add(view, 0, 0))); 52 | assert.throws(TypeError, (() => Atomics.sub(view, 0, 0))); 53 | assert.throws(TypeError, (() => Atomics.and(view, 0, 0))); 54 | assert.throws(TypeError, (() => Atomics.or(view, 0, 0))); 55 | assert.throws(TypeError, (() => Atomics.xor(view, 0, 0))); 56 | assert.throws(TypeError, (() => Atomics.wait(view, 0, 0))); 57 | assert.throws(TypeError, (() => Atomics.wake(view, 0))); 58 | } 59 | 60 | // BEGIN EPILOGUE 61 | finishTest("atomics-on-other-stuff"); 62 | // END EPILOGUE 63 | -------------------------------------------------------------------------------- /test262/atomics-on-shared-nonint-arrays.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("atomics-on-shared-nonint-arrays"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test atomic operations on shared non-integer TypedArrays 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | 16 | var other_views = [Uint8ClampedArray, Float32Array, Float64Array]; 17 | 18 | for ( var View of other_views ) { 19 | var view = new View(sab); 20 | 21 | assert.throws(TypeError, (() => Atomics.load(view, 0))); 22 | assert.throws(TypeError, (() => Atomics.store(view, 0, 0))); 23 | assert.throws(TypeError, (() => Atomics.exchange(view, 0, 0))); 24 | assert.throws(TypeError, (() => Atomics.compareExchange(view, 0, 0, 0))); 25 | assert.throws(TypeError, (() => Atomics.add(view, 0, 0))); 26 | assert.throws(TypeError, (() => Atomics.sub(view, 0, 0))); 27 | assert.throws(TypeError, (() => Atomics.and(view, 0, 0))); 28 | assert.throws(TypeError, (() => Atomics.or(view, 0, 0))); 29 | assert.throws(TypeError, (() => Atomics.xor(view, 0, 0))); 30 | assert.throws(TypeError, (() => Atomics.wait(view, 0, 0))); 31 | assert.throws(TypeError, (() => Atomics.wake(view, 0))); 32 | } 33 | 34 | // BEGIN EPILOGUE 35 | finishTest("atomics-on-shared-nonint-arrays"); 36 | // END EPILOGUE 37 | -------------------------------------------------------------------------------- /test262/atomics-range.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("atomics-range"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test range checking of atomic operations (on arrays that allow atomic operations) 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(4); 15 | 16 | var views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; 17 | 18 | var bad_indices = [ (view) => -1, 19 | (view) => view.length, 20 | (view) => view.length*2, 21 | (view) => undefined, 22 | (view) => Number.NaN, 23 | (view) => Number.POSITIVE_INFINITY, 24 | (view) => Number.NEGATIVE_INFINITY, 25 | (view) => '3.5', 26 | (view) => 3.5, 27 | (view) => { password: "qumquat" }, 28 | (view) => ({ valueOf: () => 125 }), 29 | (view) => ({ toString: () => '125', valueOf: false }) // non-callable valueOf triggers invocation of toString 30 | ]; 31 | 32 | for ( var vidx=0 ; vidx < views.length ; vidx++ ) { 33 | var View = views[vidx]; 34 | var view = new View(sab); 35 | 36 | for ( var iidx=0 ; iidx < bad_indices.length ; iidx++ ) { 37 | var Idx = bad_indices[iidx](view); 38 | assert.throws(RangeError, () => Atomics.store(view, Idx, 10)); 39 | assert.throws(RangeError, () => Atomics.load(view, Idx)); 40 | assert.throws(RangeError, () => Atomics.exchange(view, Idx, 5)); 41 | assert.throws(RangeError, () => Atomics.compareExchange(view, Idx, 5, 8)); 42 | assert.throws(RangeError, () => Atomics.add(view, Idx, 10)); 43 | assert.throws(RangeError, () => Atomics.sub(view, Idx, 5)); 44 | assert.throws(RangeError, () => Atomics.and(view, Idx, 3)); 45 | assert.throws(RangeError, () => Atomics.or(view, Idx, 6)); 46 | assert.throws(RangeError, () => Atomics.xor(view, Idx, 2)); 47 | } 48 | } 49 | 50 | var good_indices = [ (view) => 0/-1, // -0 51 | (view) => '-0', 52 | (view) => view.length - 1, 53 | (view) => ({ valueOf: () => 0 }), 54 | (view) => ({ toString: () => '0', valueOf: false }) // non-callable valueOf triggers invocation of toString 55 | ]; 56 | 57 | for ( var vidx=0 ; vidx < views.length ; vidx++ ) { 58 | var View = views[vidx]; 59 | var view = new View(sab); 60 | 61 | for ( var iidx=0 ; iidx < good_indices.length ; iidx++ ) { 62 | var Idx = good_indices[iidx](view); 63 | Atomics.store(view, Idx, 37); 64 | assert.sameValue(Atomics.load(view, Idx), 37); 65 | assert.sameValue(Atomics.exchange(view, Idx, 37), 37); 66 | assert.sameValue(Atomics.compareExchange(view, Idx, 37, 37), 37); 67 | assert.sameValue(Atomics.add(view, Idx, 0), 37); 68 | assert.sameValue(Atomics.sub(view, Idx, 0), 37); 69 | assert.sameValue(Atomics.and(view, Idx, -1), 37); 70 | assert.sameValue(Atomics.or(view, Idx, 0), 37); 71 | assert.sameValue(Atomics.xor(view, Idx, 0), 37); 72 | } 73 | } 74 | 75 | // BEGIN EPILOGUE 76 | finishTest("atomics-range"); 77 | // END EPILOGUE 78 | -------------------------------------------------------------------------------- /test262/dataview-props.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("dataview-props"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test the properties of DataView views on shared memory 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | 16 | // Create DataView on SharedArrayBuffer 17 | 18 | var view = new DataView(sab); 19 | 20 | assert.sameValue(view.buffer, sab); 21 | assert.sameValue(view.byteLength, sab.byteLength); 22 | assert.sameValue(view.byteOffset, 0); 23 | 24 | var u8 = new Uint8Array(sab); 25 | 26 | u8[1] = 1; 27 | u8[2] = 2; 28 | 29 | // Read value from DataView on SharedArrayBuffer 30 | 31 | assert.sameValue(view.getUint16(1, true), 257); 32 | 33 | // Write value to DataView on SharedArrayBuffer 34 | 35 | view.setUint16(3, 257, true); 36 | assert.sameValue(u8[3], 1); 37 | assert.sameValue(u8[4], 2); 38 | 39 | // BEGIN EPILOGUE 40 | finishTest("dataview-props"); 41 | // END EPILOGUE 42 | -------------------------------------------------------------------------------- /test262/futex-misc.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-misc"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test miscellaneous aspects of futex operations 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(4); 15 | var view = new Int32Array(sab); 16 | 17 | // Testing the actual functioning of these requires workers, see 18 | // ../test-html/ for more test cases. 19 | 20 | // Legal corner cases for Atomics.wake(). 21 | 22 | assert.sameValue(Atomics.wake(view, 0, -3), 0); 23 | assert.sameValue(Atomics.wake(view, 0, Number.POSITIVE_INFINITY), 0); 24 | assert.sameValue(Atomics.wake(view, 0), 0); 25 | 26 | // BEGIN EPILOGUE 27 | finishTest("futex-misc"); 28 | // END EPILOGUE 29 | -------------------------------------------------------------------------------- /test262/futex-on-nonshared-int-arrays.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-on-nonshared-int-arrays"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test futex operations on non-shared integer TypedArrays 12 | ---*/ 13 | 14 | var ab = new ArrayBuffer(16); 15 | 16 | var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; 17 | 18 | for ( var View of int_views ) { 19 | var view = new View(ab); 20 | 21 | assert.throws(TypeError, (() => Atomics.wait(view, 0, 0))); 22 | assert.throws(TypeError, (() => Atomics.wake(view, 0))); 23 | } 24 | 25 | // BEGIN EPILOGUE 26 | finishTest("futex-on-nonshared-int-arrays"); 27 | // END EPILOGUE 28 | -------------------------------------------------------------------------------- /test262/futex-on-other-stuff.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-on-other-stuff"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test futex operations on values other than TypedArrays 12 | ---*/ 13 | 14 | var values = [null, 15 | undefined, 16 | true, 17 | false, 18 | new Boolean(true), 19 | 10, 20 | 3.14, 21 | new Number(4), 22 | "Hi there", 23 | new Date, 24 | /a*utomaton/g, 25 | { password: "qumquat" }, 26 | new DataView(new ArrayBuffer(10)), 27 | new ArrayBuffer(128), 28 | new SharedArrayBuffer(128), 29 | new Error("Ouch"), 30 | [1,1,2,3,5,8], 31 | ((x) => -x), 32 | new Map(), 33 | new Set(), 34 | new WeakMap(), 35 | new WeakSet(), 36 | this.Promise ? new Promise(() => "done") : undefined, 37 | Symbol("halleluja"), 38 | // TODO: Proxy? 39 | Object, 40 | Int32Array, 41 | Date, 42 | Math, 43 | Atomics ]; 44 | 45 | for ( var i=0 ; i < values.length ; i++ ) { 46 | var view = values[i]; 47 | assert.throws(TypeError, (() => Atomics.wait(view, 0, 0))); 48 | assert.throws(TypeError, (() => Atomics.wake(view, 0))); 49 | } 50 | 51 | // BEGIN EPILOGUE 52 | finishTest("futex-on-other-stuff"); 53 | // END EPILOGUE 54 | -------------------------------------------------------------------------------- /test262/futex-on-shared-nonint-arrays.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-on-shared-nonint-arrays"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test futex operations on shared non-integer TypedArrays 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | 16 | var other_views = [Uint8ClampedArray, Float32Array, Float64Array]; 17 | 18 | for ( var View of other_views ) { 19 | var view = new View(sab); 20 | 21 | assert.throws(TypeError, (() => Atomics.wait(view, 0, 0))); 22 | assert.throws(TypeError, (() => Atomics.wake(view, 0))); 23 | } 24 | 25 | // BEGIN EPILOGUE 26 | finishTest("futex-on-shared-nonint-arrays"); 27 | // END EPILOGUE 28 | -------------------------------------------------------------------------------- /test262/futex-on-shared-nonint32-arrays.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-on-shared-nonint32-arrays"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test atomic operations on shared non-int32 arrays 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(16); 15 | 16 | var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Uint32Array]; 17 | 18 | for ( var View of int_views ) { 19 | var view = new View(sab); 20 | 21 | assert.throws(TypeError, (() => Atomics.wait(view, 0, 0))); 22 | assert.throws(TypeError, (() => Atomics.wake(view, 0))); 23 | } 24 | 25 | // BEGIN EPILOGUE 26 | finishTest("futex-on-shared-nonint32-arrays"); 27 | // END EPILOGUE 28 | -------------------------------------------------------------------------------- /test262/futex-range.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-range"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test range checking of futex operations (on arrays that allow atomic operations) 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(4); 15 | var view = new Int32Array(sab); 16 | 17 | var bad_indices = [ 18 | (view) => -1, 19 | (view) => view.length, 20 | (view) => view.length*2, 21 | (view) => undefined, 22 | (view) => '3.5', 23 | (view) => 3.5, 24 | (view) => ({ password: "qumquat" }), 25 | (view) => ({ valueOf: () => 125 }), 26 | (view) => ({ toString: () => '125', valueOf: false }) // non-callable valueOf triggers invocation of toString 27 | ]; 28 | 29 | for ( var iidx=0 ; iidx < bad_indices.length ; iidx++ ) { 30 | var Idx = bad_indices[iidx](view); 31 | assert.throws(RangeError, () => Atomics.wait(view, Idx, 10)); 32 | assert.throws(RangeError, () => Atomics.wake(view, Idx)); 33 | } 34 | 35 | var good_indices = [ (view) => 0/-1, // -0 36 | (view) => '-0', 37 | (view) => view.length - 1, 38 | (view) => ({ valueOf: () => 0 }), 39 | (view) => ({ toString: () => '0', valueOf: false }) // non-callable valueOf triggers invocation of toString 40 | ]; 41 | 42 | for ( var iidx=0 ; iidx < good_indices.length ; iidx++ ) { 43 | var Idx = good_indices[iidx](view); 44 | //Atomics.wait(view, Idx, 10); // Will not block, but may throw on the main thread 45 | Atomics.wake(view, Idx); // Wakes none 46 | } 47 | 48 | 49 | // BEGIN EPILOGUE 50 | finishTest("futex-range"); 51 | // END EPILOGUE 52 | -------------------------------------------------------------------------------- /test262/futex-wait-return.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("futex-wait-return"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test some return values of Atomics.wait() 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(4); 15 | var view = new Int32Array(sab); 16 | 17 | // Testing for "ok" requires workers, see ../test-html/ for more test 18 | // cases. 19 | 20 | assert.sameValue(Atomics.wait(view, 0, 42), "not-equal"); 21 | assert.sameValue(Atomics.wait(view, 0, 0, 100), "timed-out"); 22 | 23 | // BEGIN EPILOGUE 24 | finishTest("futex-wait-return"); 25 | // END EPILOGUE 26 | -------------------------------------------------------------------------------- /test262/harness.js: -------------------------------------------------------------------------------- 1 | // This mimics, superficially, the test262 harness 2 | 3 | var _count = 0; 4 | var _errors = 0; 5 | 6 | var assert = { 7 | 8 | sameValue: function (got, expected) { 9 | 10 | function same(a, b) { 11 | var pass = true; 12 | if (typeof a != typeof b) 13 | pass = false; 14 | else if (typeof a == "number") { 15 | if (a != b) 16 | pass = isNaN(a) && isNaN(b); 17 | else if (a == 0) 18 | pass = (1/a == 1/b); 19 | } 20 | else if (typeof a == "string" || typeof a == "boolean" || typeof a == "undefined") 21 | pass = (a == b); 22 | else if (Array.isArray(a) && Array.isArray(b)) { 23 | if (a.length != b.length) 24 | pass = false; 25 | else { 26 | var limit = a.length; 27 | for ( var i=0 ; i < limit && pass ; i++ ) 28 | pass = same(a[i], b[i]); 29 | } 30 | } 31 | // TODO: view types! 32 | else 33 | pass = (a == b); 34 | return pass; 35 | } 36 | 37 | ++_count; 38 | if (!same(got, expected)) 39 | fail("Failed: got " + got + ", expected " + expected); 40 | }, 41 | 42 | throws: function (error, thunk) { 43 | ++_count; 44 | var failed = false; 45 | var failure; 46 | try { 47 | thunk(); 48 | } 49 | catch (e) { 50 | failed = true; 51 | failure = e; 52 | } 53 | if (!failed) 54 | fail("Failed: expected " + error.name + " exception, did not fail: " + thunk); 55 | if (!(failure instanceof error)) 56 | fail("Failed: expected " + error.name + ", got " + failure); 57 | } 58 | }; 59 | 60 | function clearCount() { 61 | _count = 0; 62 | _errors = 0; 63 | } 64 | 65 | function getCount() { 66 | return _count; 67 | } 68 | 69 | // Hooks that are overridden by the HTML test runner 70 | 71 | function beginTest() { 72 | } 73 | 74 | function finishTest() { 75 | } 76 | 77 | function classifyTest() {} 78 | 79 | function fail(s) { 80 | msg(s); 81 | if (++_errors >= 100) { 82 | msg("BAILING OUT"); 83 | throw new Error(s); 84 | } 85 | } 86 | 87 | function msg(s) { 88 | if (this.console && this.console.log) 89 | this.console.log(s); 90 | } 91 | -------------------------------------------------------------------------------- /test262/is-lock-free-corner-cases.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("is-lock-free-corner-cases"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test isLockFree on various non-intuitive arguments 12 | ---*/ 13 | 14 | assert.sameValue(false, Atomics.isLockFree(hide(3, Number.NaN))); 15 | assert.sameValue(false, Atomics.isLockFree(hide(3, -1))); 16 | assert.sameValue(false, Atomics.isLockFree(hide(3, 3.14))); 17 | assert.sameValue(false, Atomics.isLockFree(hide(3, 0))); 18 | 19 | assert.sameValue(Atomics.isLockFree('1'), Atomics.isLockFree(1)); 20 | assert.sameValue(Atomics.isLockFree('3'), Atomics.isLockFree(3)); 21 | 22 | assert.sameValue(Atomics.isLockFree(true), Atomics.isLockFree(1)); 23 | 24 | assert.sameValue(Atomics.isLockFree(1), Atomics.isLockFree({valueOf: () => 1})); 25 | assert.sameValue(Atomics.isLockFree(3), Atomics.isLockFree({valueOf: () => 3})); 26 | assert.sameValue(Atomics.isLockFree(1), Atomics.isLockFree({toString: () => '1'})); 27 | assert.sameValue(Atomics.isLockFree(3), Atomics.isLockFree({toString: () => '3'})); 28 | 29 | function hide(k, x) { 30 | if (k) 31 | return hide(k-3, x) + x; 32 | return 0; 33 | } 34 | 35 | // BEGIN EPILOGUE 36 | finishTest("is-lock-free-corner-cases"); 37 | // END EPILOGUE 38 | 39 | -------------------------------------------------------------------------------- /test262/is-lock-free-good.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("is-lock-free-good"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test isLockFree on nonnegative integer arguments 12 | ---*/ 13 | 14 | var sizes = [ 1, 2, 3, 4, 5, 6, 7, 8, 15 | 9, 10, 11, 12]; 16 | var answers = [ {}, {}, false, true, false, false, false, false, 17 | false, false, false, false]; 18 | 19 | function testIsLockFree() { 20 | var saved = {}; 21 | 22 | // This should defeat most optimizations. 23 | 24 | for ( var i=0 ; i < sizes.length ; i++ ) { 25 | var v = Atomics.isLockFree(sizes[i]); 26 | var a = answers[i]; 27 | assert.sameValue(typeof v, 'boolean'); 28 | if (typeof a == 'boolean') 29 | assert.sameValue(v, a); 30 | else 31 | saved[sizes[i]] = v; 32 | } 33 | 34 | // This ought to be optimizable. Make sure the answers are the same 35 | // as for the unoptimized case. 36 | 37 | assert.sameValue(Atomics.isLockFree(1), saved[1]); 38 | assert.sameValue(Atomics.isLockFree(2), saved[2]); 39 | assert.sameValue(Atomics.isLockFree(3), false); 40 | assert.sameValue(Atomics.isLockFree(4), true); 41 | assert.sameValue(Atomics.isLockFree(5), false); 42 | assert.sameValue(Atomics.isLockFree(6), false); 43 | assert.sameValue(Atomics.isLockFree(7), false); 44 | assert.sameValue(Atomics.isLockFree(8), false); 45 | assert.sameValue(Atomics.isLockFree(9), false); 46 | assert.sameValue(Atomics.isLockFree(10), false); 47 | assert.sameValue(Atomics.isLockFree(11), false); 48 | assert.sameValue(Atomics.isLockFree(12), false); 49 | } 50 | 51 | testIsLockFree(); 52 | 53 | // BEGIN EPILOGUE 54 | finishTest("is-lock-free-good"); 55 | // END EPILOGUE 56 | 57 | -------------------------------------------------------------------------------- /test262/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | SharedArrayBuffer and Atomics 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 24 | 25 | 26 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /test262/shared-array-buffer-conversion.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | /*--- 5 | es7id: TBD 6 | description: > 7 | Test conversion of a SharedArrayBuffer object to various types 8 | ---*/ 9 | 10 | // BEGIN PROLOGUE 11 | beginTest("shared-array-buffer-conversion"); 12 | // END PROLOGUE 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | 16 | assert.sameValue(String(sab), "[object SharedArrayBuffer]"); 17 | 18 | assert.sameValue(Number(sab), Number.NaN); 19 | 20 | // BEGIN EPILOGUE 21 | finishTest("shared-array-buffer-conversion"); 22 | // END EPILOGUE 23 | -------------------------------------------------------------------------------- /test262/shared-array-buffer-instance-props.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | /*--- 5 | es7id: TBD 6 | description: > 7 | Test properties and methods of the SharedArrayBuffer instances 8 | ---*/ 9 | 10 | // BEGIN PROLOGUE 11 | beginTest("shared-array-buffer-instance-props"); 12 | // END PROLOGUE 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | 16 | assert.sameValue(sab.byteLength, 1024); 17 | 18 | assert.sameValue(sab.constructor, SharedArrayBuffer); 19 | 20 | assert.sameValue(sab.slice, SharedArrayBuffer.prototype.slice); 21 | 22 | assert.sameValue(typeof sab.slice, "function"); 23 | 24 | // Not implemented in Firefox 46 25 | if (sab.slice) { 26 | // TODO: test SharedArrayBuffer.prototype.slice 27 | } 28 | 29 | // BEGIN EPILOGUE 30 | finishTest("shared-array-buffer-instance-props"); 31 | // END EPILOGUE 32 | -------------------------------------------------------------------------------- /test262/shared-array-buffer-props.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | /*--- 5 | es7id: TBD 6 | description: > 7 | Test properties of the SharedArrayBuffer object 8 | ---*/ 9 | 10 | // BEGIN PROLOGUE 11 | beginTest("shared-array-buffer-props"); 12 | // END PROLOGUE 13 | 14 | assert.sameValue(typeof SharedArrayBuffer.isView, "function"); 15 | assert.sameValue(typeof SharedArrayBuffer.prototype, "object"); 16 | assert.sameValue(typeof SharedArrayBuffer.prototype == null, false); 17 | 18 | // BEGIN EPILOGUE 19 | finishTest("shared-array-buffer-props"); 20 | // END EPILOGUE 21 | -------------------------------------------------------------------------------- /test262/shell-runner: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | for i in "shared-array-buffer-props.js" \ 3 | "shared-array-buffer-instance-props.js" \ 4 | "shared-array-buffer-conversion.js" \ 5 | "typedarray-props.js" \ 6 | "typedarray-props-range.js" \ 7 | "dataview-props.js" \ 8 | "atomics-on-good-arrays.js" \ 9 | "atomics-on-nonshared-int-arrays.js" \ 10 | "atomics-on-shared-nonint-arrays.js" \ 11 | "atomics-on-other-stuff.js" \ 12 | "atomics-range.js" \ 13 | "futex-on-shared-nonint32-arrays.js" \ 14 | "futex-on-nonshared-int-arrays.js" \ 15 | "futex-on-shared-nonint-arrays.js" \ 16 | "futex-on-other-stuff.js" \ 17 | "futex-range.js" \ 18 | "futex-misc.js" \ 19 | "futex-wait-return.js" \ 20 | "is-lock-free-good.js" \ 21 | "is-lock-free-corner-cases.js" \ 22 | "wait-wake.js" 23 | do 24 | echo "" 25 | echo "=================================================" 26 | echo $i 27 | # This is correct for the SpiderMonkey shell, contributions for 28 | # other shells are welcome. 29 | $JSSHELL -f harness.js -f agenttest_spidermonkey.js -f $i 30 | done 31 | -------------------------------------------------------------------------------- /test262/typedarray-props-range.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("typedarray-props-range"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test out-of-range access on typed array views on shared memory 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(8); 15 | 16 | var views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array, 17 | Uint8ClampedArray, Float64Array, Float32Array]; 18 | 19 | var i = 0; 20 | for ( var View of views ) { 21 | var view = new View(sab); 22 | 23 | // Note, non-index numbers are added as expandos, so avoid those. 24 | 25 | // No exception should be thrown here 26 | 27 | assert.sameValue(view[-1] = 5, 5); 28 | assert.sameValue(view[view.length] = 5, 5); 29 | assert.sameValue(view['10000'] = 5, 5); 30 | 31 | // Nor exceptions here 32 | 33 | assert.sameValue(view[-1], undefined); 34 | assert.sameValue(view[view.length], undefined); 35 | assert.sameValue(view['10000'], undefined); 36 | } 37 | 38 | // BEGIN EPILOGUE 39 | finishTest("typedarray-props-range"); 40 | // END EPILOGUE 41 | -------------------------------------------------------------------------------- /test262/typedarray-props.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("typedarray-props"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test the properties of typed array views on shared memory 12 | ---*/ 13 | 14 | var sab = new SharedArrayBuffer(1024); 15 | 16 | var int_views = [Int8Array, Uint8Array, Int16Array, Uint16Array, Int32Array, Uint32Array]; 17 | 18 | var other_views = [Uint8ClampedArray, Float32Array, Float64Array]; 19 | 20 | ////////////////////////////////////////////////////////////////////// 21 | // 22 | // Test that the views can be constructed on shared memory and that 23 | // the memory extracted from the views is the original shared buffer, 24 | // and other basic things. 25 | 26 | for ( var View of int_views ) 27 | testView(View); 28 | 29 | for ( var View of other_views ) 30 | testView(View); 31 | 32 | function testView(View) { 33 | var view = new View(sab); 34 | 35 | assert.sameValue(view.buffer, sab); 36 | assert.sameValue(view.length, sab.byteLength / View.BYTES_PER_ELEMENT); 37 | assert.sameValue(view.byteOffset, 0); 38 | 39 | var half = sab.byteLength/2; 40 | var view2 = new View(sab, half); 41 | 42 | assert.sameValue(view2.buffer, sab); 43 | assert.sameValue(view2.length, half / View.BYTES_PER_ELEMENT); 44 | assert.sameValue(view2.byteOffset, half) 45 | 46 | var view3 = new View(sab, View.BYTES_PER_ELEMENT, 5); 47 | 48 | assert.sameValue(view3.buffer, sab); 49 | assert.sameValue(view3.length, 5); 50 | assert.sameValue(view3.byteOffset, View.BYTES_PER_ELEMENT); 51 | } 52 | 53 | //////////////////////////////////////////////////////////// 54 | // 55 | // Basic element read/write. 56 | 57 | for ( var View of int_views ) { 58 | var view = new View(sab, 8, 10); 59 | 60 | var tmp = new Uint8Array(sab, 8, 1); 61 | assert.sameValue(tmp[0], 0); 62 | view[0] = -1; 63 | assert.sameValue(tmp[0], 255); 64 | view[0] = 0; 65 | assert.sameValue(tmp[0], 0); 66 | } 67 | 68 | var cuview = new Uint8ClampedArray(sab, 8, 10); 69 | cuview[3] = 1; 70 | assert.sameValue(cuview[3], 1); 71 | cuview[3] = -1; 72 | assert.sameValue(cuview[3], 0); 73 | cuview[3] = 4711; 74 | assert.sameValue(cuview[3], 255); 75 | cuview[3] = 0; 76 | 77 | var f64view = new Float64Array(sab, 8, 10); 78 | f64view[3] = Math.PI; 79 | assert.sameValue(f64view[3], Math.PI); 80 | f64view[3] = 0; 81 | 82 | var f32view = new Float32Array(sab, 8, 10); 83 | f32view[0] = Math.PI; 84 | assert.sameValue(f32view[0], Math.fround(Math.PI)); 85 | f32view[0] = 0; 86 | 87 | //////////////////////////////////////////////////////////// 88 | 89 | // TypedArray methods: 90 | // 91 | // All existing TypedArray methods should work the same (in a 92 | // single-threaded setting) if the underlying buffer is a shared 93 | // buffer, so I'm delegating most functional tests to existing test 94 | // suites (that will have to be adapted mildly). 95 | 96 | // The spec has a change to the set() method to prevent overwriting 97 | // elements when the underlying memory of two SharedArrayBuffers is 98 | // the same (as can happen if a SAB is sent to another agent and then 99 | // back to the first one; we end up with two SAB objects that 100 | // reference the same memory). That change can't be tested in a 101 | // shell, it needs to be tested in a browser. 102 | 103 | 104 | // BEGIN EPILOGUE 105 | finishTest("typedarray-props"); 106 | // END EPILOGUE 107 | -------------------------------------------------------------------------------- /test262/wait-wake.js: -------------------------------------------------------------------------------- 1 | // Copyright (C) 2015 Mozilla Corporation. All rights reserved. 2 | // This code is governed by the BSD license found in the LICENSE file. 3 | 4 | // BEGIN PROLOGUE 5 | beginTest("wait-wake"); 6 | // END PROLOGUE 7 | 8 | /*--- 9 | es7id: TBD 10 | description: > 11 | Test Atomics.wait and Atomics.wake 12 | ---*/ 13 | 14 | // Test that "wait" actually waits and does not spuriously wake up 15 | // when the memory value is changed. 16 | 17 | AgentTest.start( 18 | ` 19 | var { sab } = AgentTest.receive(); 20 | var ia = new Int32Array(sab); 21 | 22 | var then = Date.now(); 23 | Atomics.wait(ia, 0, 0); 24 | var diff = Date.now() - then; // Should be about 1000 ms 25 | AgentTest.report(diff); 26 | 27 | AgentTest.leaving(); 28 | `); 29 | 30 | var ia = new Int32Array(new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT)); 31 | AgentTest.broadcast(ia.buffer); 32 | 33 | var then = Date.now(); 34 | AgentTest.sleep(500); // Give the agent a chance to wait 35 | Atomics.store(ia, 0, 1); // Change the value, should not wake the agent 36 | AgentTest.sleep(500); // Wait some more so that we can tell 37 | Atomics.wake(ia, 0); // Really wake it up 38 | AgentTest.sleep(500); // Give it time to report back 39 | assert.sameValue(Math.abs((AgentTest.getReport()|0) - 1000) < 100, true); 40 | 41 | // BEGIN EPILOGUE 42 | finishTest("wait-wake"); 43 | // END EPILOGUE 44 | --------------------------------------------------------------------------------