What is the actual use of the Atomics object in ECMAScript?

The ECMAScript specification defines an Atomics object in section 24.4 .

Among all global objects, it is darker for me, since I did not know about its existence until I read its specification, and also Google does not have many references to it (or, perhaps, the name is too general and everything is immersed in water?).

According to his official definition

The Atomics object provides functions that work indivisibly (atomically) in the cells of an array of shared memory, as well as functions that allow agents to wait and send primitive events.

Thus, it has the form of an object with many methods for processing low-level memory and regulating access to it. And also its public interface makes me guess this. But what is the actual use of such an object for the end user? Why is this public? Are there any examples where this might be useful?

thank

+11
source share
4 answers

Atomics for synchronizing web workers who share memory. They result in memory access in the SharedArrayBuffer being thread safe. Shared memory makes multithreading much more useful because:

  • No need to copy data to stream to streams
  • Streams can communicate without using an event loop

:

var arr = new SharedArrayBuffer(1024);

// send a reference to the memory to any number of webworkers
workers.forEach(worker => worker.postMessage(arr));

// Normally, simultaneous access to the memory from multiple threads 
// (where at least one access is a write)
// is not safe, but the Atomics methods are thread-safe.
// This adds 2 to element 0 of arr.
Atomics.add(arr, 0, 2)

SharedArrayBuffer , Spectre , , Spectre.

, . Chrome 67, 68.

+11

- , WebWorkers, , .

, Atomics, , WebVorkers (, ). ArrayBuffer, SharedArrayBuffer, Atomics , , .

, :

  • - (, , ..)

, :

!

+6

, -II , Atomics , JavaScript - - ( ). , JS, C/C++ , -, JavaScript API C/C++/OtherLanguage.

+4

- " " .

let i=0;

i++

i++ 3

  1. i
  2. i 1

, 2 ? 1 .

Javascript, ?

! JavaScript , / JavaScript (Worker Threads, Web Workers).

Chrome Node ( v8) Isolate , context.

, share memory - ArrayBuffer/SharedArrayBuffer

?

> = 10

node --experimental_worker example.js

const { isMainThread, Worker, workerData } = require('worker_threads');

if (isMainThread) {
  const shm = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
  process.on('exit', () => {
    const res = new Int32Array(shm);
    console.log(res[0]); // expected 5 * 500,000 = 2,500,000
  });
  Array(5).fill(null).map(() => new Worker(__filename, { workerData: shm }));
} else {
  const arr = new Int32Array(workerData);
  for (let i = 0; i < 500000; i++) {
    arr[i]++;
  }
}

2,500,000 , 2,5 , , , , , , , , , . , , .

, n .

Atomic , .

:

const { isMainThread, Worker, workerData } = require('worker_threads');


if (isMainThread) {
    const shm = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
    process.on('exit', () => {
        const res = new Int32Array(shm);
        console.log(res[0]); // expected 5 * 500,000 = 2,500,000
    });
    Array(5).fill(null).map(() => new Worker(__filename, { workerData: shm }));
} else {
    const arr = new Int32Array(workerData);
    for (let i = 0; i < 500000; i++) {
        Atomics.add(arr, 0, 1);
    }
}

2,500,000

, Mutex Atomics

, 1 ,

class Mutex {

    /**
     * 
     * @param {Mutex} mutex 
     * @param {Int32Array} resource 
     * @param {number} onceFlagCell 
     * @param {(done)=>void} cb
     */
    static once(mutex, resource, onceFlagCell, cb) {
        if (Atomics.load(resource, onceFlagCell) === 1) {
            return;
        }
        mutex.lock();
        // maybe someone already flagged it
        if (Atomics.load(resource, onceFlagCell) === 1) {
            mutex.unlock();
            return;
        }
        cb(() => {
            Atomics.store(resource, onceFlagCell, 1);
            mutex.unlock();
        });
    }
    /**
     * 
     * @param {Int32Array} resource 
     * @param {number} cell 
     */
    constructor(resource, cell) {
        this.resource = resource;
        this.cell = cell;
        this.lockAcquired = false;
    }

    /**
     * locks the mutex
     */
    lock() {
        if (this.lockAcquired) {
            console.warn('you already acquired the lock you stupid');
            return;
        }
        const { resource, cell } = this;
        while (true) {
            // lock is already acquired, wait
            if (Atomics.load(resource, cell) > 0) {
                while ('ok' !== Atomics.wait(resource, cell, 0));
            }
            const countOfAcquiresBeforeMe = Atomics.add(resource, cell, 1);
            // someone was faster than me, try again later
            if (countOfAcquiresBeforeMe >= 1) {
                Atomics.sub(resource, cell, 1);
                continue;
            }
            this.lockAcquired = true;
            return;
        }
    }

    /**
     * unlocks the mutex
     */
    unlock() {
        if (!this.lockAcquired) {
            console.warn('you didn\'t acquire the lock you stupid');
            return;
        }
        Atomics.sub(this.resource, this.cell, 1);
        Atomics.notify(this.resource, this.cell, 1);
        this.lockAcquired = false;
    }
}

Now you need to select SharedArrayBufferand divide them between all the threads and see that every time only 1 thread passes incritical section

Run with Node> 10

node --experimental_worker example.js

const { isMainThread, Worker, workerData, threadId } = require('worker_threads');


const { promisify } = require('util');
const doSomethingFakeThatTakesTimeAndShouldBeAtomic = promisify(setTimeout);

if (isMainThread) {
    const shm = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
    Array(5).fill(null).map(() => new Worker(__filename, { workerData: shm }));
} else {
    (async () => {
        const arr = new Int32Array(workerData);
        const mutex = new Mutex(arr, 0);
        mutex.lock();
        console.log('[${threadId}] ${new Date().toISOString()}');
        await doSomethingFakeThatTakesTimeAndShouldBeAtomic(1000);
        mutex.unlock();
    })();
}
+2
source

Source: https://habr.com/ru/post/1692429/


All Articles