Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Commit

Permalink
rewrite state handlng into string-string KV store
Browse files Browse the repository at this point in the history
This is the first phase of #144, to narrow the API we need from a backing
store. All state mutations go through the keepers, and the keepers deal
exclusively with an object that uses string keys and string values. We still
JSON-serialize this object on demand (at the end of the batch/block).

The next phase will replace the buildVatController/buildKernel APIs to accept
a Storage object, with get/set/getRange/deleteRange methods, and have the
JSON-serialized backing store object live outside the kernel (in the host). A
subsequent phase will introduce commitCrank/commitBlock methods on that
Storage object, then a final phase will replace the JSON backing store with a
proper database.
  • Loading branch information
warner committed Sep 27, 2019
1 parent 429a20a commit 1660051
Show file tree
Hide file tree
Showing 6 changed files with 658 additions and 211 deletions.
68 changes: 44 additions & 24 deletions src/kernel/state/deviceKeeper.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
import harden from '@agoric/harden';
import Nat from '@agoric/nat';
import { insist } from '../../insist';
import { parseKernelSlot } from '../parseKernelSlots';
import { makeVatSlot, parseVatSlot } from '../../parseVatSlots';
import { insistDeviceID } from '../id';

export function initializeDeviceState(deviceID, state) {
state.kernelSlotToDevSlot = {}; // kdNN -> d+NN, koNN -> o-NN
state.devSlotToKernelSlot = {}; // d+NN -> kdNN, o-NN -> koNN
state.nextObjectID = 10; // for imports, the NN in o-NN
export function initializeDeviceState(state, deviceID) {
state[`${deviceID}.o.nextID`] = '10';
}

export function makeDeviceKeeper(state, deviceID, addKernelDeviceNode) {
insistDeviceID(deviceID);

function stateHasKey(name) {
return Object.prototype.hasOwnProperty.call(state, name);
}

function mapDeviceSlotToKernelSlot(devSlot) {
insist(`${devSlot}` === devSlot, 'non-string devSlot');
// kdebug(`mapOutbound ${devSlot}`);
const existing = state.devSlotToKernelSlot[devSlot];
if (existing === undefined) {
const devKey = `${deviceID}.c.${devSlot}`;
if (!stateHasKey(devKey)) {
const { type, allocatedByVat } = parseVatSlot(devSlot);

if (allocatedByVat) {
Expand All @@ -31,60 +34,77 @@ export function makeDeviceKeeper(state, deviceID, addKernelDeviceNode) {
} else {
throw new Error(`unknown type ${type}`);
}
state.devSlotToKernelSlot[devSlot] = kernelSlot;
state.kernelSlotToDevSlot[kernelSlot] = devSlot;
const kernelKey = `${deviceID}.c.${kernelSlot}`;
state[kernelKey] = devSlot;
state[devKey] = kernelSlot;
} else {
// the vat didn't allocate it, and the kernel didn't allocate it
// (else it would have been in the c-list), so it must be bogus
throw new Error(`unknown devSlot ${devSlot}`);
}
}

return state.devSlotToKernelSlot[devSlot];
return state[devKey];
}

// mapInbound: convert from absolute slot to deviceName-relative slot. This
// is used when building the arguments for dispatch.invoke.
function mapKernelSlotToDeviceSlot(kernelSlot) {
insist(`${kernelSlot}` === kernelSlot, 'non-string kernelSlot');
const existing = state.kernelSlotToDevSlot[kernelSlot];
if (existing === undefined) {
const kernelKey = `${deviceID}.c.${kernelSlot}`;
if (!stateHasKey(kernelKey)) {
const { type } = parseKernelSlot(kernelSlot);

let devSlot;
let id;
if (type === 'object') {
const id = state.nextObjectID;
state.nextObjectID += 1;
devSlot = makeVatSlot(type, false, id);
id = Nat(Number(state[`${deviceID}.o.nextID`]));
state[`${deviceID}.o.nextID`] = `${id + 1}`;
} else if (type === 'device') {
throw new Error('devices cannot import other device nodes');
} else if (type === 'promise') {
throw new Error('devices cannot import Promises');
} else {
throw new Error(`unknown type ${type}`);
}
const devSlot = makeVatSlot(type, false, id);

state.devSlotToKernelSlot[devSlot] = kernelSlot;
state.kernelSlotToDevSlot[kernelSlot] = devSlot;
const devKey = `${deviceID}.c.${devSlot}`;
state[devKey] = kernelSlot;
state[kernelKey] = devSlot;
}

return state.kernelSlotToDevSlot[kernelSlot];
return state[kernelKey];
}

function getDeviceState() {
return state.deviceState;
// this should return an object, generally CapData, or undefined
const key = `${deviceID}.deviceState`;
if (stateHasKey(key)) {
return JSON.parse(state[key]);
// todo: formalize the CapData, and store .deviceState.body, and
// .deviceState.slots as 'vatSlot[,vatSlot..]'
}
return undefined;
}

function setDeviceState(value) {
state.deviceState = value;
state[`${deviceID}.deviceState`] = JSON.stringify(value);
}

function dumpState() {
const res = [];
Object.getOwnPropertyNames(state.kernelSlotToDevSlot).forEach(ks => {
const ds = state.kernelSlotToDevSlot[ks];
res.push([ks, deviceID, ds]);
});
const prefix = `${deviceID}.c.`;
// todo: db.getKeys(start='${deviceID}.c.', end='${deviceID}.c/')
for (const k of Object.getOwnPropertyNames(state)) {
if (k.startsWith(prefix)) {
const slot = k.slice(prefix.length);
if (!slot.startsWith('k')) {
const devSlot = slot;
const kernelSlot = state[k];
res.push([kernelSlot, deviceID, devSlot]);
}
}
}
return harden(res);
}

Expand Down
Loading

0 comments on commit 1660051

Please sign in to comment.