diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2020-05-12 11:09:28 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-05-12 11:09:28 -0400 |
commit | 593c3aa857bec2992aba7b7ec588dc031e379572 (patch) | |
tree | f0c3773ee7d8cd6e727837778ca5f7e6e75b90ae /core/core.js | |
parent | 35e8bc8de65b523e082d446dc25f114ed81264a8 (diff) |
Clean up core/shared_queue.js (#5237)
Diffstat (limited to 'core/core.js')
-rw-r--r-- | core/core.js | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/core/core.js b/core/core.js new file mode 100644 index 000000000..4c6f708bb --- /dev/null +++ b/core/core.js @@ -0,0 +1,203 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +/* +SharedQueue Binary Layout ++-------------------------------+-------------------------------+ +| NUM_RECORDS (32) | ++---------------------------------------------------------------+ +| NUM_SHIFTED_OFF (32) | ++---------------------------------------------------------------+ +| HEAD (32) | ++---------------------------------------------------------------+ +| OFFSETS (32) | ++---------------------------------------------------------------+ +| RECORD_ENDS (*MAX_RECORDS) ... ++---------------------------------------------------------------+ +| RECORDS (*MAX_RECORDS) ... ++---------------------------------------------------------------+ + */ + +/* eslint-disable @typescript-eslint/no-use-before-define */ + +((window) => { + const MAX_RECORDS = 100; + const INDEX_NUM_RECORDS = 0; + const INDEX_NUM_SHIFTED_OFF = 1; + const INDEX_HEAD = 2; + const INDEX_OFFSETS = 3; + const INDEX_RECORDS = INDEX_OFFSETS + 2 * MAX_RECORDS; + const HEAD_INIT = 4 * INDEX_RECORDS; + + // Available on start due to bindings. + const core = window.Deno.core; + const { recv, send } = core; + + let sharedBytes; + let shared32; + + let asyncHandlers; + + let initialized = false; + + function maybeInit() { + if (!initialized) { + init(); + initialized = true; + } + } + + function init() { + const shared = core.shared; + assert(shared.byteLength > 0); + assert(sharedBytes == null); + assert(shared32 == null); + sharedBytes = new Uint8Array(shared); + shared32 = new Int32Array(shared); + asyncHandlers = []; + // Callers should not call core.recv, use setAsyncHandler. + recv(handleAsyncMsgFromRust); + } + + function ops() { + // op id 0 is a special value to retrieve the map of registered ops. + const opsMapBytes = send(0, new Uint8Array([]), null); + const opsMapJson = String.fromCharCode.apply(null, opsMapBytes); + return JSON.parse(opsMapJson); + } + + function assert(cond) { + if (!cond) { + throw Error("assert"); + } + } + + function reset() { + maybeInit(); + shared32[INDEX_NUM_RECORDS] = 0; + shared32[INDEX_NUM_SHIFTED_OFF] = 0; + shared32[INDEX_HEAD] = HEAD_INIT; + } + + function head() { + maybeInit(); + return shared32[INDEX_HEAD]; + } + + function numRecords() { + return shared32[INDEX_NUM_RECORDS]; + } + + function size() { + return shared32[INDEX_NUM_RECORDS] - shared32[INDEX_NUM_SHIFTED_OFF]; + } + + function setMeta(index, end, opId) { + shared32[INDEX_OFFSETS + 2 * index] = end; + shared32[INDEX_OFFSETS + 2 * index + 1] = opId; + } + + function getMeta(index) { + if (index < numRecords()) { + const buf = shared32[INDEX_OFFSETS + 2 * index]; + const opId = shared32[INDEX_OFFSETS + 2 * index + 1]; + return [opId, buf]; + } else { + return null; + } + } + + function getOffset(index) { + if (index < numRecords()) { + if (index == 0) { + return HEAD_INIT; + } else { + const prevEnd = shared32[INDEX_OFFSETS + 2 * (index - 1)]; + return (prevEnd + 3) & ~3; + } + } else { + return null; + } + } + + function push(opId, buf) { + const off = head(); + const end = off + buf.byteLength; + const alignedEnd = (end + 3) & ~3; + const index = numRecords(); + if (alignedEnd > shared32.byteLength || index >= MAX_RECORDS) { + // console.log("shared_queue.js push fail"); + return false; + } + setMeta(index, end, opId); + assert(alignedEnd % 4 === 0); + assert(end - off == buf.byteLength); + sharedBytes.set(buf, off); + shared32[INDEX_NUM_RECORDS] += 1; + shared32[INDEX_HEAD] = alignedEnd; + return true; + } + + /// Returns null if empty. + function shift() { + const i = shared32[INDEX_NUM_SHIFTED_OFF]; + if (size() == 0) { + assert(i == 0); + return null; + } + + const off = getOffset(i); + const [opId, end] = getMeta(i); + + if (size() > 1) { + shared32[INDEX_NUM_SHIFTED_OFF] += 1; + } else { + reset(); + } + + assert(off != null); + assert(end != null); + const buf = sharedBytes.subarray(off, end); + return [opId, buf]; + } + + function setAsyncHandler(opId, cb) { + maybeInit(); + assert(opId != null); + asyncHandlers[opId] = cb; + } + + function handleAsyncMsgFromRust(opId, buf) { + if (buf) { + // This is the overflow_response case of deno::Isolate::poll(). + asyncHandlers[opId](buf); + } else { + while (true) { + const opIdBuf = shift(); + if (opIdBuf == null) { + break; + } + assert(asyncHandlers[opIdBuf[0]] != null); + asyncHandlers[opIdBuf[0]](opIdBuf[1]); + } + } + } + + function dispatch(opId, control, zeroCopy = null) { + return send(opId, control, zeroCopy); + } + + Object.assign(window.Deno.core, { + setAsyncHandler, + dispatch, + ops, + // sharedQueue is private but exposed for testing. + sharedQueue: { + MAX_RECORDS, + head, + numRecords, + size, + push, + reset, + shift, + }, + }); +})(this); |