Является ли `postMessage` или уступкой в ​​цикле событий или аналогичной синхронизации общей памяти? - PullRequest
0 голосов
/ 21 сентября 2018

Я ничего не вижу в спецификации JavaScript , предложенных расширениях спецификации DOM , относящихся к SharedArrayBuffer, или текущей спецификации WHAT-WG HTML предположить, что совместно используемая память будет синхронизироваться / обновляться между потоками, когда один поток отправляет сообщение другому, а другой обрабатывает сообщение.( После одна уже отправила общую память другой.) Однако я также не могу экспериментально проверить, что не не происходит (в моих тестах я нене вижу устаревших значений).Есть ли какая-то такая гарантия, что я упускаю, и если да, то где она гарантирована?Например, документировано ли это для postMessage, и я пропустил это, или есть что-то о возвращении к циклу событий / очереди заданий, которое гарантирует это (поскольку обработка сообщения из другого потока включает в себя это) и т. Д.?Или, альтернативно, это определенно не гарантировано (и эта информация где-то в спецификации)?

Пожалуйста не спекулируйте и не делайте«разумное предположение».Я ищу точную информацию: цитаты из канонических источников, воспроизводимый эксперимент, который показывает, что это не гарантировано (хотя, я полагаю, тогда возникает вопрос, является ли это просто ошибкой реализации), такого рода вещи.


Ниже приведен источник для моих тестов, которые еще не смогли перехватить несинхронизированную память.Чтобы запустить его, вам нужно использовать браузер, который в настоящее время поддерживает SharedArrayBuffer, что, на мой взгляд, в настоящий момент означает Chrome v67 или выше (Firefox, Edge и Safari все имели поддержку, но отключили его в ответ на Spectre и Meltdown.в январе 2018 года; Chrome сделал то же самое, но повторно включил его в v67 [июль 2018] на платформах, где включена их функция изоляции сайтов).

sync-test-postMessage.html:

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Sync Test postMessage</title>
</head>
<body>
<script src="sync-test-postMessage-main.js"></script>
</body>
</html>

sync-test-postMessage-main.js:

const array = new Uint32Array(new SharedArrayBuffer(Uint32Array.BYTES_PER_ELEMENT));
const worker = new Worker("./sync-test-postMessage-worker.js");
let counter = 0;
const limit = 1000000;
const report = Math.floor(limit / 10);
let mismatches = 0;
const now = performance.now();
const log = msg => {
    console.log(`${msg} - ${mismatches} mismatch(es) - ${performance.now() - now}ms`);
};
worker.addEventListener("message", e => {
    if (e.data && e.data.type === "ping") {
        ++counter;
        const value = array[0];
        if (counter !== value) {
            ++mismatches;
            console.log(`Out of sync! ${counter} !== ${value}`);
        }
        if (counter % report === 0) {
            log(`${counter} of ${limit}`);
        }
        if (counter < limit) {
            worker.postMessage({type: "pong"});
        } else {
            console.log("done");
        }
    }
});
worker.postMessage({type: "init", array});
console.log(`running to ${limit}`);

sync-test-postMessage-worker.js:

let array;
this.addEventListener("message", e => {
    if (e.data) {
        switch (e.data.type) {
            case "init":
                array = e.data.array;
                // fall through to "pong"
            case "pong":
                ++array[0];
                this.postMessage({type: "ping"});
                break;
        }
    }
});

Используя этот код, если память не была синхронизирована, я бы ожидал, что в какой-то момент основной поток увидитустаревшее значение в общем массиве.Но вполне вероятно (на мой взгляд), что этот код работает только из-за относительно больших временных интервалов, связанных с передачей сообщений ...

1 Ответ

0 голосов
/ 26 октября 2018

TL; DR: Да, это так.


В ветка на es-обсуждения , автор предложения о совместной памяти, ЛарсХансен писал:

В браузере отправка и получение postMessage всегда предназначались для создания фронта синхронизации таким же образом, как и для записи-чтения.http://tc39.github.io/ecmascript_sharedmem/shmem.html#WebBrowserEmbedding

Не уверен, где эта проза закончилась, когда спецификация была передана в документ es262.

Я добавил:

Спасибо!

Похоже, что это хотя бы частично здесь: https://tc39.github.io/ecma262/#sec-host-synchronizes-with

Итак, вопрос (ну, два вопроса) только для тех из нас, кто не глубоко разбирается в терминологии раздела Модель памяти,Дано:

  1. Поток A отправляет общий поток 1 КБ в Поток B через postMessage
  2. Запись B потока в различные местоположения в этом блоке напрямую (не через Atomics.store)
  3. Поток B выполняет postMessage для потока A (без ссылки на блок в postMessage)
  4. Поток A получает сообщение и считывает данные из блока (не через Atomics.load)

... я прав, что на шаге 4 гарантируется, что поток A будет надежно видеть записи в этот блок потоком B с шага 2, поскольку postMessage был«край синхронизации», обеспечивающий (помимо прочего) актуальность кэшей CPU L1d и т. д .?

Аналогично, если (!) я правильно читаю, в вашем примере с Mandlebrot выиметь Atomics.wait в одном месте в общем блоке, и когда поток просыпается, он предполагает, что другие данные в блоке (не в диапазоне wait) могут быть надежно прочитаны напрямую.Это также «край синхронизации»?

На что он ответил:

... гарантируя (среди прочего), что кэши CPU L1d работаютна сегодняшний день и т. д .?

Да, это было намерением этого языка.Запись в память должна происходить - до того, как postMessage и приемное сообщение должны произойти - до чтения.

... Это также "край синхронизации"?

Да, тот же аргумент.Запись происходит - перед пробуждением, а пробуждение ожидания - перед чтением.

Все это сделано для того, чтобы данные могли быть записаны и прочитаны с использованием дешевых несинхронизированных операций записи и чтения, а такжезатем для (сравнительно дорогой) синхронизации, чтобы обеспечить надлежащую наблюдаемость.

...