Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify and pluralize promise resolution syscalls #2242

Merged
merged 2 commits into from
Jan 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions packages/SwingSet/src/kernel/cleanup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { kdebug } from './kdebug';
// import { kdebug } from './kdebug';
import { parseKernelSlot } from './parseKernelSlots';

// XXX temporary flags to control features during development
Expand Down Expand Up @@ -65,36 +65,36 @@ export function deleteCListEntryIfEasy(
export function getKpidsToRetire(kernelKeeper, rootKPID, rootKernelData) {
const seen = new Set();
function scanKernelPromise(kpid, kernelData) {
kdebug(`### scanning ${kpid} ${JSON.stringify(kernelData)}`);
// kdebug(`### scanning ${kpid} ${JSON.stringify(kernelData)}`);
seen.add(kpid);
if (kernelData) {
for (const slot of kernelData.slots) {
const { type } = parseKernelSlot(slot);
kdebug(`## examine ${kpid} slot ${slot}`);
// kdebug(`## examine ${kpid} slot ${slot}`);
if (type === 'promise') {
if (!seen.has(slot)) {
const kp = kernelKeeper.getKernelPromise(slot);
const { data, state } = kp;
kdebug(`## state of ${slot} is: ${JSON.stringify(kp)}`);
// kdebug(`## state of ${slot} is: ${JSON.stringify(kp)}`);
if (state !== 'unresolved') {
if (data) {
scanKernelPromise(slot, data);
}
} else {
kdebug(`## ${slot} is still unresolved`);
// kdebug(`## ${slot} is still unresolved`);
}
} else {
kdebug(`## ${slot} previously seen`);
// kdebug(`## ${slot} previously seen`);
}
} else {
kdebug(`## ${slot} is not a promise`);
// kdebug(`## ${slot} is not a promise`);
}
}
} else {
kdebug(`## ${kpid} has no data`);
// kdebug(`## ${kpid} has no data`);
}
}
kdebug(`## scanning ${rootKPID}`);
// kdebug(`## scanning ${rootKPID}`);
scanKernelPromise(rootKPID, rootKernelData);
return Array.from(seen);
}
15 changes: 1 addition & 14 deletions packages/SwingSet/src/kernel/kernel.js
Original file line number Diff line number Diff line change
Expand Up @@ -447,19 +447,6 @@ export default function buildKernel(
}
}

function statNameForNotify(state) {
switch (state) {
case 'fulfilledToPresence':
return 'dispatchNotifyFulfillToPresence';
case 'fulfilledToData':
return 'dispatchNotifyFulfillToData';
case 'rejected':
return 'dispatchNotifyReject';
default:
throw Error(`unknown promise state ${state}`);
}
}

async function processNotify(message) {
const { vatID, kpid } = message;
insistVatID(vatID);
Expand All @@ -470,7 +457,7 @@ export default function buildKernel(
kdebug(`dropping notify of ${kpid} to ${vatID} because vat is dead`);
} else {
const p = kernelKeeper.getKernelPromise(kpid);
kernelKeeper.incStat(statNameForNotify(p.state));
kernelKeeper.incStat('dispatchNotify');
const vatKeeper = kernelKeeper.getVatKeeper(vatID);

assert(p.state !== 'unresolved', details`spurious notification ${kpid}`);
Expand Down
105 changes: 50 additions & 55 deletions packages/SwingSet/src/kernel/kernelSyscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,58 +100,59 @@ export function makeKernelSyscallHandler(tools) {
return OKNULL;
}

function fulfillToPresence(vatID, kpid, targetSlot) {
insistVatID(vatID);
insistKernelType('promise', kpid);
insistKernelType('object', targetSlot);
kernelKeeper.incStat('syscalls');
kernelKeeper.incStat('syscallFulfillToPresence');
const p = kernelKeeper.getResolveablePromise(kpid, vatID);
const { subscribers, queue } = p;
kernelKeeper.fulfillKernelPromiseToPresence(kpid, targetSlot);
notifySubscribersAndQueue(kpid, vatID, subscribers, queue);
// todo: some day it'd be nice to delete the promise table entry now. To
// do that correctly, we must make sure no vats still hold pointers to
// it, which means vats must drop their refs when they get notified about
// the resolution ("you knew it was resolved, you shouldn't be sending
// any more messages to it, send them to the resolution instead"), and we
// must wait for those notifications to be delivered.
if (p.policy === 'logAlways') {
console.log(`${kpid}.policy logAlways: fulfillToPresence ${targetSlot}`);
function extractPresenceIfPresent(data) {
const body = JSON.parse(data.body);
if (
body &&
typeof body === 'object' &&
body['@qclass'] === 'slot' &&
body.index === 0
) {
if (data.slots.length === 1) {
const slot = data.slots[0];
const { type } = parseKernelSlot(slot);
if (type === 'object') {
return slot;
}
}
}
return OKNULL;
return null;
}

function fulfillToData(vatID, kpid, data) {
function resolve(vatID, resolutions) {
insistVatID(vatID);
insistKernelType('promise', kpid);
insistCapData(data);
kernelKeeper.incStat('syscalls');
kernelKeeper.incStat('syscallFulfillToData');
const p = kernelKeeper.getResolveablePromise(kpid, vatID);
const { subscribers, queue } = p;
let idx = 0;
for (const dataSlot of data.slots) {
kernelKeeper.incrementRefCount(dataSlot, `fulfill|s${idx}`);
idx += 1;
kernelKeeper.incStat('syscallResolve');
for (const resolution of resolutions) {
const [kpid, rejected, data] = resolution;
insistKernelType('promise', kpid);
insistCapData(data);
if (rejected) {
resolveToError(kpid, data, vatID);
} else {
const p = kernelKeeper.getResolveablePromise(kpid, vatID);
const { subscribers, queue } = p;
let idx = 0;
for (const dataSlot of data.slots) {
kernelKeeper.incrementRefCount(dataSlot, `resolve|s${idx}`);
FUDCo marked this conversation as resolved.
Show resolved Hide resolved
idx += 1;
}
const presence = extractPresenceIfPresent(data);
if (presence) {
kernelKeeper.fulfillKernelPromiseToPresence(kpid, presence);
} else if (rejected) {
kernelKeeper.rejectKernelPromise(kpid, data);
} else {
kernelKeeper.fulfillKernelPromiseToData(kpid, data);
}
notifySubscribersAndQueue(kpid, vatID, subscribers, queue);
if (p.policy === 'logAlways') {
console.log(
`${kpid}.policy logAlways: resolve ${JSON.stringify(data)}`,
);
}
}
}
kernelKeeper.fulfillKernelPromiseToData(kpid, data);
notifySubscribersAndQueue(kpid, vatID, subscribers, queue);
if (p.policy === 'logAlways') {
console.log(
`${kpid}.policy logAlways: fulfillToData ${JSON.stringify(data)}`,
);
}
return OKNULL;
}

function reject(vatID, kpid, data) {
insistVatID(vatID);
insistKernelType('promise', kpid);
insistCapData(data);
kernelKeeper.incStat('syscalls');
kernelKeeper.incStat('syscallReject');
resolveToError(kpid, data, vatID);
return OKNULL;
}

Expand All @@ -164,12 +165,8 @@ export function makeKernelSyscallHandler(tools) {
return invoke(...args);
case 'subscribe':
return subscribe(...args);
case 'fulfillToPresence':
return fulfillToPresence(...args);
case 'fulfillToData':
return fulfillToData(...args);
case 'reject':
return reject(...args);
case 'resolve':
return resolve(...args);
case 'exit':
return exit(...args);
case 'vatstoreGet':
Expand All @@ -187,9 +184,7 @@ export function makeKernelSyscallHandler(tools) {
send, // TODO remove these individual ones
invoke,
subscribe,
fulfillToPresence,
fulfillToData,
reject,
resolve,
doKernelSyscall,
});
return kernelSyscallHandler;
Expand Down
29 changes: 2 additions & 27 deletions packages/SwingSet/src/kernel/liveSlots.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
/* global HandledPromise */

import {
QCLASS,
Remotable,
Far,
getInterfaceOf,
Expand Down Expand Up @@ -476,32 +475,8 @@ function build(syscall, forVatID, cacheSize, vatPowers, vatParameters) {
return res => {
harden(res);
lsdebug(`ls.thenResolve fired`, res);

// We need to know if this is resolving to an imported/exported
// presence, because then the kernel can deliver queued messages. We
// could build a simpler way of doing this.
const ser = m.serialize(res);
lsdebug(` ser ${ser.body} ${JSON.stringify(ser.slots)}`);
// find out what resolution category we're using
const unser = JSON.parse(ser.body);
if (
Object(unser) === unser &&
QCLASS in unser &&
unser[QCLASS] === 'slot'
) {
const slot = ser.slots[unser.index];
insistVatType('object', slot);
syscall.fulfillToPresence(promiseID, slot);
} else {
// if it resolves to data, .thens fire but kernel-queued messages are
// rejected, because you can't send messages to data
syscall.fulfillToData(promiseID, ser);
}

// If we were *also* waiting on this promise (perhaps we received it as
// an argument, and also as a result=), then we are responsible for
// notifying ourselves. The kernel assumes we're a grownup and don't
// need to be reminded of something we did ourselves.
syscall.resolve([[promiseID, false, ser]]);
FUDCo marked this conversation as resolved.
Show resolved Hide resolved
const pRec = importedPromisesByPromiseID.get(promiseID);
if (pRec) {
pRec.resolve(res);
Expand All @@ -515,7 +490,7 @@ function build(syscall, forVatID, cacheSize, vatPowers, vatParameters) {
harden(rej);
lsdebug(`ls thenReject fired`, rej);
const ser = m.serialize(rej);
syscall.reject(promiseID, ser);
syscall.resolve([[promiseID, true, ser]]);
const pRec = importedPromisesByPromiseID.get(promiseID);
if (pRec) {
pRec.reject(rej);
Expand Down
7 changes: 1 addition & 6 deletions packages/SwingSet/src/kernel/state/kernelKeeper.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,16 +155,11 @@ export default function makeKernelKeeper(storage, kernelSlog) {
syscalls: 0,
syscallSend: 0,
syscallSubscribe: 0,
syscallFulfillToData: 0,
syscallFulfillToPresence: 0,
syscallReject: 0,
syscallResolve: 0,
syscallCallNow: 0,
dispatches: 0,
dispatchDeliver: 0,
dispatchNotify: 0,
dispatchNotifyFulfillToData: 0,
dispatchNotifyFulfillToPresence: 0,
dispatchNotifyReject: 0,
clistEntries: 0,
clistEntriesUp: 0,
clistEntriesDown: 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ parentPort.on('message', ([type, ...margs]) => {
throw Error(`nodeWorker cannot syscall.callNow`);
},
subscribe: (...args) => doSyscall(['subscribe', ...args]),
fulfillToData: (...args) => doSyscall(['fulfillToData', ...args]),
fulfillToPresence: (...args) => doSyscall(['fulfillToPresence', ...args]),
reject: (...args) => doSyscall(['reject', ...args]),
resolve: (...args) => doSyscall(['resolve', ...args]),
});

const vatID = 'demo-vatID';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,7 @@ fromParent.on('data', ([type, ...margs]) => {
throw Error(`nodeWorker cannot syscall.callNow`);
},
subscribe: (...args) => doSyscall(['subscribe', ...args]),
fulfillToData: (...args) => doSyscall(['fulfillToData', ...args]),
fulfillToPresence: (...args) => doSyscall(['fulfillToPresence', ...args]),
reject: (...args) => doSyscall(['reject', ...args]),
resolve: (...args) => doSyscall(['resolve', ...args]),
});

const vatID = 'demo-vatID';
Expand Down
4 changes: 1 addition & 3 deletions packages/SwingSet/src/kernel/vatManager/syscall.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,7 @@ export function createSyscall(transcriptManager) {
send: (...args) => doSyscall(['send', ...args]),
callNow: (...args) => doSyscall(['callNow', ...args]),
subscribe: (...args) => doSyscall(['subscribe', ...args]),
fulfillToData: (...args) => doSyscall(['fulfillToData', ...args]),
fulfillToPresence: (...args) => doSyscall(['fulfillToPresence', ...args]),
reject: (...args) => doSyscall(['reject', ...args]),
resolve: (...args) => doSyscall(['resolve', ...args]),
exit: (...args) => doSyscall(['exit', ...args]),
vatstoreGet: (...args) => doSyscall(['vatstoreGet', ...args]),
vatstoreSet: (...args) => doSyscall(['vatstoreSet', ...args]),
Expand Down
Loading