2020-01-02 15:13:47 -05:00
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
2019-04-21 12:16:55 -04:00
/ *
SharedQueue Binary Layout
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - + -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
| NUM _RECORDS ( 32 ) |
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
| NUM _SHIFTED _OFF ( 32 ) |
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
| HEAD ( 32 ) |
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
| OFFSETS ( 32 ) |
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
| RECORD _ENDS ( * MAX _RECORDS ) ...
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
| RECORDS ( * MAX _RECORDS ) ...
+ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - +
* /
2019-03-26 08:22:07 -04:00
2019-04-24 21:43:06 -04:00
/* eslint-disable @typescript-eslint/no-use-before-define */
2020-03-28 13:03:49 -04:00
( ( window ) => {
2019-03-14 19:17:52 -04:00
const MAX _RECORDS = 100 ;
const INDEX _NUM _RECORDS = 0 ;
const INDEX _NUM _SHIFTED _OFF = 1 ;
const INDEX _HEAD = 2 ;
const INDEX _OFFSETS = 3 ;
2019-08-07 14:02:29 -04:00
const INDEX _RECORDS = INDEX _OFFSETS + 2 * MAX _RECORDS ;
2019-03-14 19:17:52 -04:00
const HEAD _INIT = 4 * INDEX _RECORDS ;
2019-08-05 07:23:41 -04:00
// Available on start due to bindings.
2020-05-12 11:09:28 -04:00
const core = window . Deno . core ;
const { recv , send } = core ;
2019-03-26 08:22:07 -04:00
let sharedBytes ;
let shared32 ;
2020-01-17 08:26:11 -05:00
let asyncHandlers ;
2019-04-24 21:43:06 -04:00
let initialized = false ;
2020-06-21 10:34:43 -04:00
let opsCache = { } ;
2020-08-07 16:47:18 -04:00
const errorMap = { } ;
2019-04-24 21:43:06 -04:00
function maybeInit ( ) {
if ( ! initialized ) {
init ( ) ;
initialized = true ;
}
}
function init ( ) {
2020-05-12 11:09:28 -04:00
const shared = core . shared ;
2019-04-24 21:43:06 -04:00
assert ( shared . byteLength > 0 ) ;
assert ( sharedBytes == null ) ;
assert ( shared32 == null ) ;
sharedBytes = new Uint8Array ( shared ) ;
shared32 = new Int32Array ( shared ) ;
2020-01-17 08:26:11 -05:00
asyncHandlers = [ ] ;
2020-05-12 11:09:28 -04:00
// Callers should not call core.recv, use setAsyncHandler.
recv ( handleAsyncMsgFromRust ) ;
2019-04-24 21:43:06 -04:00
}
2019-03-14 19:17:52 -04:00
2019-09-30 14:59:44 -04:00
function ops ( ) {
2020-01-02 07:48:46 -05:00
// op id 0 is a special value to retrieve the map of registered ops.
2020-08-21 05:47:57 -04:00
const opsMapBytes = send ( 0 ) ;
2019-09-30 14:59:44 -04:00
const opsMapJson = String . fromCharCode . apply ( null , opsMapBytes ) ;
2020-06-21 10:34:43 -04:00
opsCache = JSON . parse ( opsMapJson ) ;
return { ... opsCache } ;
2019-09-30 14:59:44 -04:00
}
2019-03-14 19:17:52 -04:00
function assert ( cond ) {
if ( ! cond ) {
throw Error ( "assert" ) ;
}
}
function reset ( ) {
2019-04-24 21:43:06 -04:00
maybeInit ( ) ;
2019-03-15 15:49:41 -04:00
shared32 [ INDEX _NUM _RECORDS ] = 0 ;
shared32 [ INDEX _NUM _SHIFTED _OFF ] = 0 ;
2019-03-14 19:17:52 -04:00
shared32 [ INDEX _HEAD ] = HEAD _INIT ;
}
function head ( ) {
2019-04-24 21:43:06 -04:00
maybeInit ( ) ;
2019-03-14 19:17:52 -04:00
return shared32 [ INDEX _HEAD ] ;
}
function numRecords ( ) {
return shared32 [ INDEX _NUM _RECORDS ] ;
}
2019-03-15 15:49:41 -04:00
function size ( ) {
return shared32 [ INDEX _NUM _RECORDS ] - shared32 [ INDEX _NUM _SHIFTED _OFF ] ;
}
2019-08-07 14:02:29 -04:00
function setMeta ( index , end , opId ) {
shared32 [ INDEX _OFFSETS + 2 * index ] = end ;
shared32 [ INDEX _OFFSETS + 2 * index + 1 ] = opId ;
2019-03-14 19:17:52 -04:00
}
2019-08-07 14:02:29 -04:00
function getMeta ( index ) {
2019-03-14 19:17:52 -04:00
if ( index < numRecords ( ) ) {
2019-08-07 14:02:29 -04:00
const buf = shared32 [ INDEX _OFFSETS + 2 * index ] ;
const opId = shared32 [ INDEX _OFFSETS + 2 * index + 1 ] ;
return [ opId , buf ] ;
2019-03-14 19:17:52 -04:00
} else {
return null ;
}
}
function getOffset ( index ) {
if ( index < numRecords ( ) ) {
if ( index == 0 ) {
return HEAD _INIT ;
} else {
2020-03-01 17:17:59 -05:00
const prevEnd = shared32 [ INDEX _OFFSETS + 2 * ( index - 1 ) ] ;
return ( prevEnd + 3 ) & ~ 3 ;
2019-03-14 19:17:52 -04:00
}
} else {
return null ;
}
}
2019-08-07 14:02:29 -04:00
function push ( opId , buf ) {
2019-09-07 12:27:18 -04:00
const off = head ( ) ;
const end = off + buf . byteLength ;
2020-03-01 17:17:59 -05:00
const alignedEnd = ( end + 3 ) & ~ 3 ;
2019-09-07 12:27:18 -04:00
const index = numRecords ( ) ;
2020-03-01 17:17:59 -05:00
if ( alignedEnd > shared32 . byteLength || index >= MAX _RECORDS ) {
2019-05-20 12:06:57 -04:00
// console.log("shared_queue.js push fail");
2019-03-14 19:17:52 -04:00
return false ;
}
2019-08-07 14:02:29 -04:00
setMeta ( index , end , opId ) ;
2020-03-01 17:17:59 -05:00
assert ( alignedEnd % 4 === 0 ) ;
2019-03-14 19:17:52 -04:00
assert ( end - off == buf . byteLength ) ;
sharedBytes . set ( buf , off ) ;
shared32 [ INDEX _NUM _RECORDS ] += 1 ;
2020-03-01 17:17:59 -05:00
shared32 [ INDEX _HEAD ] = alignedEnd ;
2019-03-14 19:17:52 -04:00
return true ;
}
/// Returns null if empty.
function shift ( ) {
2019-09-07 12:27:18 -04:00
const i = shared32 [ INDEX _NUM _SHIFTED _OFF ] ;
2019-03-15 15:49:41 -04:00
if ( size ( ) == 0 ) {
assert ( i == 0 ) ;
2019-03-14 19:17:52 -04:00
return null ;
}
2019-03-15 15:49:41 -04:00
2019-08-07 14:02:29 -04:00
const off = getOffset ( i ) ;
const [ opId , end ] = getMeta ( i ) ;
2019-03-14 19:17:52 -04:00
2019-03-15 15:49:41 -04:00
if ( size ( ) > 1 ) {
shared32 [ INDEX _NUM _SHIFTED _OFF ] += 1 ;
} else {
reset ( ) ;
}
2019-03-26 08:22:07 -04:00
assert ( off != null ) ;
assert ( end != null ) ;
2019-08-07 14:02:29 -04:00
const buf = sharedBytes . subarray ( off , end ) ;
return [ opId , buf ] ;
2019-03-14 19:17:52 -04:00
}
2020-01-17 08:26:11 -05:00
function setAsyncHandler ( opId , cb ) {
2019-04-24 21:43:06 -04:00
maybeInit ( ) ;
2020-01-17 08:26:11 -05:00
assert ( opId != null ) ;
asyncHandlers [ opId ] = cb ;
2019-03-14 19:17:52 -04:00
}
2019-08-07 14:02:29 -04:00
function handleAsyncMsgFromRust ( opId , buf ) {
2019-03-14 19:17:52 -04:00
if ( buf ) {
2020-09-06 15:44:29 -04:00
// This is the overflow_response case of deno::JsRuntime::poll().
2020-01-17 08:26:11 -05:00
asyncHandlers [ opId ] ( buf ) ;
2019-03-14 19:17:52 -04:00
} else {
2019-08-07 14:02:29 -04:00
while ( true ) {
2019-09-07 12:27:18 -04:00
const opIdBuf = shift ( ) ;
2019-08-07 14:02:29 -04:00
if ( opIdBuf == null ) {
break ;
}
2020-01-17 08:26:11 -05:00
assert ( asyncHandlers [ opIdBuf [ 0 ] ] != null ) ;
asyncHandlers [ opIdBuf [ 0 ] ] ( opIdBuf [ 1 ] ) ;
2019-03-14 19:17:52 -04:00
}
2019-03-14 19:17:52 -04:00
}
}
2020-06-21 10:34:43 -04:00
function dispatch ( opName , control , ... zeroCopy ) {
return send ( opsCache [ opName ] , control , ... zeroCopy ) ;
}
2020-08-26 12:20:22 -04:00
function registerErrorClass ( errorName , className ) {
2020-08-07 16:47:18 -04:00
if ( typeof errorMap [ errorName ] !== "undefined" ) {
throw new TypeError ( ` Error class for " ${ errorName } " already registered ` ) ;
}
2020-08-26 12:20:22 -04:00
errorMap [ errorName ] = className ;
2020-08-07 16:47:18 -04:00
}
function getErrorClass ( errorName ) {
2020-10-05 05:35:51 -04:00
return errorMap [ errorName ] ;
2020-08-07 16:47:18 -04:00
}
2020-08-20 09:45:59 -04:00
// Returns Uint8Array
function encodeJson ( args ) {
const s = JSON . stringify ( args ) ;
return core . encode ( s ) ;
}
function decodeJson ( ui8 ) {
2020-09-16 16:22:43 -04:00
const s = core . decode ( ui8 ) ;
2020-08-20 09:45:59 -04:00
return JSON . parse ( s ) ;
}
let nextPromiseId = 1 ;
const promiseTable = { } ;
2020-10-05 05:35:51 -04:00
function processResponse ( res ) {
if ( "ok" in res ) {
return res . ok ;
} else {
const ErrorClass = getErrorClass ( res . err . className ) ;
if ( ! ErrorClass ) {
throw new Error (
` Unregistered error class: " ${ res . err . className } " \n ${ res . err . message } \n Classes of errors returned from ops should be registered via Deno.core.registerErrorClass(). ` ,
) ;
}
throw new ErrorClass ( res . err . message ) ;
}
}
2020-09-16 16:22:43 -04:00
async function jsonOpAsync ( opName , args = { } , ... zeroCopy ) {
2020-08-20 09:45:59 -04:00
setAsyncHandler ( opsCache [ opName ] , jsonOpAsyncHandler ) ;
args . promiseId = nextPromiseId ++ ;
const argsBuf = encodeJson ( args ) ;
2020-08-21 05:47:57 -04:00
dispatch ( opName , argsBuf , ... zeroCopy ) ;
2020-08-20 09:45:59 -04:00
let resolve , reject ;
const promise = new Promise ( ( resolve _ , reject _ ) => {
resolve = resolve _ ;
reject = reject _ ;
} ) ;
promise . resolve = resolve ;
promise . reject = reject ;
promiseTable [ args . promiseId ] = promise ;
2020-10-05 05:35:51 -04:00
return processResponse ( await promise ) ;
2020-08-20 09:45:59 -04:00
}
2020-09-16 16:22:43 -04:00
function jsonOpSync ( opName , args = { } , ... zeroCopy ) {
2020-08-20 09:45:59 -04:00
const argsBuf = encodeJson ( args ) ;
2020-08-21 05:47:57 -04:00
const res = dispatch ( opName , argsBuf , ... zeroCopy ) ;
2020-10-05 05:35:51 -04:00
return processResponse ( decodeJson ( res ) ) ;
2020-08-20 09:45:59 -04:00
}
function jsonOpAsyncHandler ( buf ) {
// Json Op.
2020-09-16 16:22:43 -04:00
const res = decodeJson ( buf ) ;
const promise = promiseTable [ res . promiseId ] ;
delete promiseTable [ res . promiseId ] ;
promise . resolve ( res ) ;
2020-08-20 09:45:59 -04:00
}
2020-09-17 12:09:50 -04:00
function resources ( ) {
return jsonOpSync ( "op_resources" ) ;
}
function close ( rid ) {
jsonOpSync ( "op_close" , { rid } ) ;
}
2020-05-12 11:09:28 -04:00
Object . assign ( window . Deno . core , {
2020-08-20 09:45:59 -04:00
jsonOpAsync ,
jsonOpSync ,
2019-03-14 19:17:52 -04:00
setAsyncHandler ,
2020-06-01 14:20:47 -04:00
dispatch : send ,
2020-06-21 10:34:43 -04:00
dispatchByName : dispatch ,
2020-05-12 11:09:28 -04:00
ops ,
2020-09-17 12:09:50 -04:00
close ,
resources ,
2020-08-07 16:47:18 -04:00
registerErrorClass ,
getErrorClass ,
2020-05-12 11:09:28 -04:00
// sharedQueue is private but exposed for testing.
2019-03-26 08:22:07 -04:00
sharedQueue : {
2019-04-21 12:16:55 -04:00
MAX _RECORDS ,
2019-03-14 19:17:52 -04:00
head ,
numRecords ,
size ,
push ,
reset ,
2020-03-28 13:03:49 -04:00
shift ,
2019-09-30 14:59:44 -04:00
} ,
2020-05-12 11:09:28 -04:00
} ) ;
2019-04-24 21:43:06 -04:00
} ) ( this ) ;