|
| 1 | +# Frequently asked questions |
| 2 | + |
| 3 | +## My racy program does not do what I expect it to do |
| 4 | + |
| 5 | +Consider two threads where one is busy-waiting on the other: |
| 6 | + |
| 7 | +```js |
| 8 | +// Thread 1 // Thread 2 |
| 9 | +while (mem[n] == 0) { ... |
| 10 | + /* do nothing */ mem[n] = 1 |
| 11 | +} ... |
| 12 | +print("Hi") |
| 13 | +``` |
| 14 | + |
| 15 | +The programmer's intent is clear: the loop in Thread 1 |
| 16 | +waits until a flag is set, and eventually Thread 2 sets the flag, |
| 17 | +signaling for Thread 1 to leave the loop. |
| 18 | + |
| 19 | +Thread 1 may never print "Hi", or it may only sometimes print "Hi" |
| 20 | +and sometimes not. (When it does not it will loop forever.) The reason |
| 21 | +is that the read in the loop does not synchronize with the write, and may |
| 22 | +therefore be lifted out of the loop by the compiler, like this: |
| 23 | + |
| 24 | +```js |
| 25 | +// Thread 1 |
| 26 | +let temp = mem[n]; |
| 27 | +while (temp == 0) { |
| 28 | + /* do nothing */ |
| 29 | +} |
| 30 | +print("Hi") |
| 31 | +``` |
| 32 | + |
| 33 | +If the write by Thread 2 is visible to Thread 1 at the time Thread 1 performs the |
| 34 | +read then the loop is skipped, but if the write comes later then it is not seen at all |
| 35 | +and the loop runs forever. |
| 36 | + |
| 37 | +The Firefox JavaScript compiler performs that optimization, so the program |
| 38 | +will run fine when it runs in the interpreter but will stop working |
| 39 | +once the code becomes compiled. |
| 40 | + |
| 41 | +The solution is to use proper synchronization: |
| 42 | + |
| 43 | +```js |
| 44 | +// Thread 1 // Thread 2 |
| 45 | +while (Atomics.load(mem, n) == 0) { ... |
| 46 | + /* do nothing */ Atomics.store(mem, n, 1); |
| 47 | +} ... |
| 48 | +print("Hi") |
| 49 | +``` |
0 commit comments