Skip to content

Commit

Permalink
feat: keep persistent comms vat state in the vatstore
Browse files Browse the repository at this point in the history
  • Loading branch information
FUDCo committed Apr 13, 2021
1 parent 51d7204 commit c55401b
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 50 deletions.
2 changes: 1 addition & 1 deletion packages/SwingSet/src/kernel/vatTranslator.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ function makeTranslateVatSyscallToKernelSyscall(vatID, kernelKeeper) {

function insistValidVatstoreKey(key) {
assert.typeof(key, 'string');
assert(key.match(/^[\w.+/]+$/));
assert(key.match(/^[-\w.+/]+$/));
}

function translateVatstoreGet(key) {
Expand Down
12 changes: 4 additions & 8 deletions packages/SwingSet/src/vats/comms/clist-inbound.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,15 @@ export function makeInbound(state) {
const remote = state.getRemote(remoteID);
const lpid = remote.mapFromRemote(flipRemoteSlot(rpid));
remote.deleteToRemoteMapping(lpid);
remote.enqueueRetirement(remote.nextSendSeqNum(), rpid);
remote.enqueueRetirement(rpid);
cdebug(`comms begin retiring ${remoteID} ${rpid} ${lpid}`);
}

function retireAcknowledgedRemotePromiseIDs(remoteID, ackSeqNum) {
const remote = state.getRemote(remoteID);
for (;;) {
const rpid = remote.nextReadyRetirement(ackSeqNum);
if (rpid) {
retireRemotePromiseID(remoteID, flipRemoteSlot(rpid));
} else {
break;
}
const readyToRetire = remote.getReadyRetirements(ackSeqNum);
for (const rpid of readyToRetire) {
retireRemotePromiseID(remoteID, flipRemoteSlot(rpid));
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/SwingSet/src/vats/comms/delivery.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,9 @@ export function makeDeliveryKit(state, syscall, transmit, clistKit) {
assert(delim1 >= 0, X`received message ${message} lacks seqNum delimiter`);
const seqNum = message.substring(0, delim1);
const remote = state.getRemote(remoteID);
remote.advanceReceivedSeqNum();
const recvSeqNum = remote.advanceReceivedSeqNum();
assert(
seqNum === '' || seqNum === `${remote.lastReceivedSeqNum()}`,
seqNum === '' || seqNum === `${recvSeqNum}`,
X`unexpected recv seqNum ${seqNum}`,
);

Expand Down
16 changes: 13 additions & 3 deletions packages/SwingSet/src/vats/comms/dispatch.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export function buildCommsDispatch(
vatParameters = {},
) {
const { identifierBase = 0, sendExplicitSeqNums = true } = vatParameters;
const state = makeState(null, identifierBase);
const state = makeState(syscall, identifierBase);
const clistKit = makeCListKit(state, syscall);

function transmit(remoteID, msg) {
Expand All @@ -41,10 +41,20 @@ export function buildCommsDispatch(

// our root object (o+0) is the Comms Controller
const controller = makeVatSlot('object', true, 0);
state.addMetaObject(controller);
cdebug(`comms controller is ${controller}`);

let needToInitializeState = true;

function initializeState() {
state.initialize();
state.addMetaObject(controller);
cdebug(`comms controller is ${controller}`);
needToInitializeState = false;
}

function deliver(target, method, args, result) {
if (needToInitializeState) {
initializeState();
}
// console.debug(`comms.deliver ${target} r=${result}`);
insistCapData(args);

Expand Down
35 changes: 21 additions & 14 deletions packages/SwingSet/src/vats/comms/remote.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,10 @@ export function makeRemote(store, remoteID) {

function advanceReceivedSeqNum() {
const key = `${remoteID}.recvSeq`;
const seqNum = Number(store.getRequired(key));
store.set(key, `${seqNum + 1}`);
let seqNum = Number(store.getRequired(key));
seqNum += 1;
store.set(key, `${seqNum}`);
return seqNum;
}

function allocateRemoteObject() {
Expand All @@ -113,25 +115,30 @@ export function makeRemote(store, remoteID) {
return makeRemoteSlot('promise', false, index);
}

function enqueueRetirement(seqNum, rpid) {
const key = `${remoteID}.rq`;
const retirementQueue = JSON.parse(store.getRequired(key));
function enqueueRetirement(rpid) {
const seqNum = nextSendSeqNum();
const queueKey = `${remoteID}.rq`;
const retirementQueue = JSON.parse(store.getRequired(queueKey));
retirementQueue.push([seqNum, rpid]);
store.set(key, JSON.stringify(retirementQueue));
store.set(queueKey, JSON.stringify(retirementQueue));
}

function nextReadyRetirement(ackSeqNum) {
function getReadyRetirements(ackSeqNum) {
const key = `${remoteID}.rq`;
const retirementQueue = JSON.parse(store.getRequired(key));
if (retirementQueue.length > 0) {
const ready = [];
while (retirementQueue.length > 0) {
const [sentSeqNum, rpid] = retirementQueue[0];
if (sentSeqNum <= ackSeqNum) {
retirementQueue.shift();
store.set(key, JSON.stringify(retirementQueue));
return rpid;
if (sentSeqNum > ackSeqNum) {
break;
}
ready.push(rpid);
retirementQueue.shift();
}
if (ready.length > 0) {
store.set(key, JSON.stringify(retirementQueue));
}
return undefined;
return ready;
}

return harden({
Expand All @@ -151,6 +158,6 @@ export function makeRemote(store, remoteID) {
lastReceivedSeqNum,
advanceReceivedSeqNum,
enqueueRetirement,
nextReadyRetirement,
getReadyRetirements,
});
}
56 changes: 34 additions & 22 deletions packages/SwingSet/src/vats/comms/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,25 +9,34 @@ import { cdebug } from './cdebug';
const COMMS = 'comms';
const KERNEL = 'kernel';

function makeEphemeralVatstore() {
const store = new Map();
function makeEphemeralSyscallVatstore() {
console.log('making fake vatstore');
const map = new Map();
return harden({
vatstoreGet: key => map.get(key),
vatstoreSet: (key, value) => map.set(key, value),
vatstoreDelete: key => map.delete(key),
});
}

function makeSyscallStore(syscall) {
return harden({
get(key) {
assert.typeof(key, 'string');
return store.get(key);
return syscall.vatstoreGet(key);
},
set(key, value) {
assert.typeof(key, 'string');
assert.typeof(value, 'string');
store.set(key, value);
syscall.vatstoreSet(key, value);
},
delete(key) {
assert.typeof(key, 'string');
return store.delete(key);
return syscall.vatstoreDelete(key);
},
getRequired(key) {
assert.typeof(key, 'string');
const result = store.get(key);
const result = syscall.vatstoreGet(key);
assert(result !== undefined, X`store lacks required key ${key}`);
return result;
},
Expand Down Expand Up @@ -57,7 +66,7 @@ function commaSplit(s) {
// record the new `p+NN` value anywhere. The counter we use for allocation
// will continue on to the next higher NN.

export function makeState(store, identifierBase = 0) {
export function makeState(syscall, identifierBase = 0) {
// Comms vat state is kept in the vatstore, which is managed by the kernel and
// accessed as part of the syscall interface. The schema used here is very
// similar to (in fact, modelled upon) the schema the kernel uses for its own
Expand Down Expand Up @@ -90,7 +99,9 @@ export function makeState(store, identifierBase = 0) {
// r.nextID = $NN // remote connection identifier allocation counter (rNN)
// r.$loid = r$NN // mapping receiver objects to remotes they receive from
// rname.$name = r$NN // mapping from remote names to remote IDs
// (remote name strings are limited to sequences of [A-Za-z0-9.+-] )
// r$NN.initialized = true // present if this remote has had its storage initialized
// r$NN.name = name // name for this remote
// r$NN.transmitterID = $kfref // transmitter object for sending to remote
// r$NN.c.$rref = $lref // r$NN inbound c-list (ro+NN/ro-NN/rp+NN/rp-NN -> loNN/lpNN)
// r$NN.c.$lref = $rref // r$NN outbound c-list (loNN/lpNN -> ro+NN/ro-NN/rp+NN/rp-NN)
Expand All @@ -100,18 +111,21 @@ export function makeState(store, identifierBase = 0) {
// r$NN.p.nextID = $NN // r$NN promise identifier allocation counter (rp-NN)
// r$NN.rq = [[$seqnum,$rpid],[$seqnum,$rpid]...] // r$NN promise retirement queue

if (!store) {
store = makeEphemeralVatstore();
if (!syscall) {
syscall = makeEphemeralSyscallVatstore();
}
const store = makeSyscallStore(syscall);

function createStartingCommsVatState() {
store.set('identifierBase', `${identifierBase}`);
store.set('lo.nextID', `${identifierBase + 10}`);
store.set('lp.nextID', `${identifierBase + 20}`);
store.set('o.nextID', `${identifierBase + 30}`);
store.set('p.nextID', `${identifierBase + 40}`);
store.set('r.nextID', '1');
store.set('initialized', 'true');
function initialize() {
if (!store.get('initialized')) {
store.set('identifierBase', `${identifierBase}`);
store.set('lo.nextID', `${identifierBase + 10}`);
store.set('lp.nextID', `${identifierBase + 20}`);
store.set('o.nextID', `${identifierBase + 30}`);
store.set('p.nextID', `${identifierBase + 40}`);
store.set('r.nextID', '1');
store.set('initialized', 'true');
}
}

function mapFromKernel(kfref) {
Expand Down Expand Up @@ -320,7 +334,7 @@ export function makeState(store, identifierBase = 0) {
}

function addRemote(name, transmitterID) {
assert(/^\w+$/.test(name), X`not a valid remote name: ${name}`);
assert(/^[-\w.+]+$/.test(name), `not a valid remote name: ${name}`);
const nameKey = `rname.${name}`;
assert(!store.get(nameKey), X`remote name ${name} already in use`);

Expand Down Expand Up @@ -354,11 +368,9 @@ export function makeState(store, identifierBase = 0) {
return store.get(`r.${receiverID}`);
}

if (!store.get('initialized')) {
createStartingCommsVatState();
}

return harden({
initialize,

mapFromKernel,
mapToKernel,
addKernelMapping,
Expand Down
10 changes: 10 additions & 0 deletions packages/SwingSet/test/commsVatDriver.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ const oCommsRoot = '@o+0'; // Always the root of the comms vat
* @returns {unknown} a syscall object
*/
function loggingSyscall(log) {
const fakestore = new Map();
return harden({
send(target, method, args, result) {
// console.log(`<< send ${target}, ${method}, ${JSON.stringify(args)}, ${result}`);
Expand All @@ -161,6 +162,15 @@ function loggingSyscall(log) {
// console.log(`<< subscribe ${slot}`);
log.push(slot);
},
vatstoreGet(key) {
return fakestore.get(key);
},
vatstoreSet(key, value) {
fakestore.set(key, value);
},
vatstoreDelete(key) {
fakestore.delete(key);
},
});
}

Expand Down
13 changes: 13 additions & 0 deletions packages/SwingSet/test/test-comms.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { commsVatDriver } from './commsVatDriver';

test('provideRemoteForLocal', t => {
const s = makeState(null, 0);
s.initialize();
const fakeSyscall = {};
const clistKit = makeCListKit(s, fakeSyscall);
const { provideRemoteForLocal } = clistKit;
Expand All @@ -23,12 +24,22 @@ test('provideRemoteForLocal', t => {

function mockSyscall() {
const sends = [];
const fakestore = new Map();
const syscall = harden({
send(targetSlot, method, args) {
sends.push([targetSlot, method, args]);
return 'r-1';
},
subscribe(_targetSlot) {},
vatstoreGet(key) {
return fakestore.get(key);
},
vatstoreSet(key, value) {
fakestore.set(key, value);
},
vatstoreDelete(key) {
fakestore.delete(key);
},
});
return { syscall, sends };
}
Expand All @@ -47,6 +58,7 @@ test('transmit', t => {
const { syscall, sends } = mockSyscall();
const d = buildCommsDispatch(syscall, 'fakestate', 'fakehelpers');
const { state, clistKit } = debugState.get(d);
state.initialize();
const {
provideKernelForLocal,
provideLocalForKernel,
Expand Down Expand Up @@ -118,6 +130,7 @@ test('receive', t => {
const { syscall, sends } = mockSyscall();
const d = buildCommsDispatch(syscall, 'fakestate', 'fakehelpers');
const { state, clistKit } = debugState.get(d);
state.initialize();
const {
provideLocalForKernel,
getKernelForLocal,
Expand Down

0 comments on commit c55401b

Please sign in to comment.