-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathawait-async.js
132 lines (113 loc) · 4.79 KB
/
await-async.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Author: Lars T Hansen, lhansen@mozilla.com
*/
/* Polyfill for Atomics.waitAsync() for web browsers.
*
* Any kind of agent that is able to create a new Worker can use this polyfill.
*
* Load this file in all agents that will use Atomics.waitAsync.
*
* Agents that don't call Atomics.waitAsync need do nothing special.
*
* Any kind of agent can wake another agent that is sleeping in
* Atomics.waitAsync by just calling Atomics.notify for the location being slept
* on, as normal.
*
* The implementation is not completely faithful to the proposed semantics: in
* the case where an agent first asyncWaits and then waits on the same location:
* when it is woken, the two waits will be woken in order, while in the real
* semantics, the sync wait will be woken first.
*
* In this polyfill Atomics.waitAsync is not very fast.
*/
/* Implementation:
*
* For every wait we fork off a Worker to perform the wait. Workers are reused
* when possible. The worker communicates with its parent using postMessage.
*/
(function () {
let helperCode = `
onmessage = function (ev) {
try {
switch (ev.data[0]) {
case 'wait': {
let [_, ia, index, value, timeout] = ev.data;
let result = Atomics.wait(ia, index, value, timeout)
postMessage(['ok', result]);
break;
}
default: {
throw new Error("Bogus message sent to wait helper: " + ev.data.join(','));
}
}
} catch (e) {
console.log("Exception in wait helper");
postMessage(['error', 'Exception']);
}
}
`;
let helpers = [];
function allocHelper() {
if (helpers.length > 0)
return helpers.pop();
let h = new Worker("data:application/javascript," + encodeURIComponent(helperCode));
return h;
}
function freeHelper(h) {
helpers.push(h);
}
// Atomics.waitAsync always returns a promise. Throws standard errors
// for parameter validation. The promise is resolved with a string as from
// Atomics.wait, or, in the case something went completely wrong, it is
// rejected with an error string.
Atomics.waitAsync = function (ia, index_, value_, timeout_) {
if (typeof ia != "object" || !(ia instanceof Int32Array) || !(ia.buffer instanceof SharedArrayBuffer))
throw new TypeError("Expected shared memory");
// These conversions only approximate the desired semantics but are
// close enough for the polyfill.
let index = index_|0;
let value = value_|0;
let timeout = timeout_ === undefined ? Infinity : +timeout_;
// Range checking for the index.
ia[index];
// Optimization, avoid the helper thread in this common case.
if (Atomics.load(ia, index) != value)
return Promise.resolve("not-equal");
// General case, we must wait.
return new Promise(function (resolve, reject) {
let h = allocHelper();
h.onmessage = function (ev) {
// Free the helper early so that it can be reused if the resolution
// needs a helper.
freeHelper(h);
switch (ev.data[0]) {
case 'ok':
resolve(ev.data[1]);
break;
case 'error':
// Note, rejection is not in the spec, it is an artifact of the polyfill.
// The helper already printed an error to the console.
reject(ev.data[1]);
break;
}
}
// It's possible to do better here if the ia is already known to the
// helper. In that case we can communicate the other data through
// shared memory and wake the agent. And it is possible to make ia
// known to the helper by waking it with a special value so that it
// checks its messages, and then posting the ia to the helper. Some
// caching / decay scheme is useful no doubt, to improve performance
// and avoid leaks.
//
// In the event we wake the helper directly, we can micro-wait here
// for a quick result. We'll need to restructure some code to make
// that work out properly, and some synchronization is necessary for
// the helper to know that we've picked up the result and no
// postMessage is necessary.
h.postMessage(['wait', ia, index, value, timeout]);
})
}
})();