Skip to content

Commit 00098da

Browse files
committed
fix: don't retire promises that resolve to data structures containing promises
1 parent a376876 commit 00098da

File tree

3 files changed

+44
-30
lines changed

3 files changed

+44
-30
lines changed

packages/SwingSet/src/kernel/liveSlots.js

+17-11
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,19 @@ function build(syscall, _state, makeRoot, forVatID) {
393393
slotToVal.delete(promiseID);
394394
}
395395

396+
function retirePromiseIDIfEasy(promiseID, data) {
397+
for (const slot of data.slots) {
398+
const { type } = parseVatSlot(slot);
399+
if (type === 'promise') {
400+
lsdebug(
401+
`Unable to retire ${promiseID} because slot ${slot} is a promise`,
402+
);
403+
return;
404+
}
405+
}
406+
retirePromiseID(promiseID);
407+
}
408+
396409
function thenResolve(promiseID) {
397410
insistVatType('promise', promiseID);
398411
return res => {
@@ -406,9 +419,6 @@ function build(syscall, _state, makeRoot, forVatID) {
406419
lsdebug(` ser ${ser.body} ${JSON.stringify(ser.slots)}`);
407420
// find out what resolution category we're using
408421
const unser = JSON.parse(ser.body);
409-
if (importedPromisesByPromiseID.has(promiseID)) {
410-
importedPromisesByPromiseID.get(promiseID).res(res);
411-
}
412422
if (
413423
Object(unser) === unser &&
414424
QCLASS in unser &&
@@ -417,12 +427,10 @@ function build(syscall, _state, makeRoot, forVatID) {
417427
const slot = ser.slots[unser.index];
418428
insistVatType('object', slot);
419429
syscall.fulfillToPresence(promiseID, slot);
420-
retirePromiseID(promiseID);
421430
} else {
422431
// if it resolves to data, .thens fire but kernel-queued messages are
423432
// rejected, because you can't send messages to data
424433
syscall.fulfillToData(promiseID, ser);
425-
retirePromiseID(promiseID);
426434
}
427435

428436
// TODO (for chip): the kernel currently notifies all subscribers of a
@@ -445,6 +453,7 @@ function build(syscall, _state, makeRoot, forVatID) {
445453
if (pRec) {
446454
pRec.resolve(res);
447455
}
456+
retirePromiseIDIfEasy(promiseID, ser);
448457
};
449458
}
450459

@@ -453,17 +462,14 @@ function build(syscall, _state, makeRoot, forVatID) {
453462
harden(rej);
454463
lsdebug(`ls thenReject fired`, rej);
455464
const ser = m.serialize(rej);
456-
if (importedPromisesByPromiseID.has(promiseID)) {
457-
importedPromisesByPromiseID.get(promiseID).rej(rej);
458-
}
459465
syscall.reject(promiseID, ser);
460-
retirePromiseID(promiseID);
461466
// TODO (for chip): this is also a double-rejection until the
462467
// retire-promises branch lands. Delete this comment when that happens.
463468
const pRec = importedPromisesByPromiseID.get(promiseID);
464469
if (pRec) {
465470
pRec.reject(rej);
466471
}
472+
retirePromiseIDIfEasy(promiseID, ser);
467473
};
468474
}
469475

@@ -480,7 +486,7 @@ function build(syscall, _state, makeRoot, forVatID) {
480486
const pRec = importedPromisesByPromiseID.get(promiseID);
481487
const val = m.unserialize(data);
482488
pRec.resolve(val);
483-
retirePromiseID(promiseID);
489+
retirePromiseIDIfEasy(promiseID, data);
484490
}
485491

486492
function notifyFulfillToPresence(promiseID, slot) {
@@ -514,7 +520,7 @@ function build(syscall, _state, makeRoot, forVatID) {
514520
const pRec = importedPromisesByPromiseID.get(promiseID);
515521
const val = m.unserialize(data);
516522
pRec.reject(val);
517-
retirePromiseID(promiseID);
523+
retirePromiseIDIfEasy(promiseID, data);
518524
}
519525

520526
// here we finally invoke the vat code, and get back the root object

packages/SwingSet/src/kernel/vatManager.js

+17-7
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,19 @@ export default function makeVatManager(
107107
}
108108
}
109109

110+
function deleteCListEntryIfEasy(kpid, vpid, kernelData) {
111+
for (const slot of kernelData.slots) {
112+
const { type } = parseKernelSlot(slot);
113+
if (type === 'promise') {
114+
kdebug(
115+
`Unable to delete clist entry ${kpid}<=>${vpid} because slot ${slot} is a promise`,
116+
);
117+
return;
118+
}
119+
}
120+
vatKeeper.deleteCListEntry(kpid, vpid);
121+
}
122+
110123
// syscall handlers: these are wrapped by the 'syscall' object and made
111124
// available to userspace
112125

@@ -182,7 +195,7 @@ export default function makeVatManager(
182195
data.body
183196
} ${JSON.stringify(data.slots)}/${JSON.stringify(kernelSlots)}`,
184197
);
185-
vatKeeper.deleteCListEntry(kpid, promiseID);
198+
deleteCListEntryIfEasy(kpid, promiseID, kernelData);
186199
syscallManager.fulfillToData(vatID, kpid, kernelData);
187200
}
188201

@@ -197,7 +210,7 @@ export default function makeVatManager(
197210
data.body
198211
} ${JSON.stringify(data.slots)}/${JSON.stringify(kernelSlots)}`,
199212
);
200-
vatKeeper.deleteCListEntry(kpid, promiseID);
213+
deleteCListEntryIfEasy(kpid, promiseID, kernelData);
201214
syscallManager.reject(vatID, kpid, kernelData);
202215
}
203216

@@ -378,7 +391,6 @@ export default function makeVatManager(
378391
['notifyFulfillToPresence', vpid, slot],
379392
`vat[${vatID}].promise[${vpid}] fulfillToPresence failed`,
380393
);
381-
vatKeeper.deleteCListEntry(kpid, vpid);
382394
} else if (kp.state === 'redirected') {
383395
throw new Error('not implemented yet');
384396
} else if (kp.state === 'fulfilledToData') {
@@ -387,24 +399,22 @@ export default function makeVatManager(
387399
...kp.data,
388400
slots: kp.data.slots.map(slot => mapKernelSlotToVatSlot(slot)),
389401
});
390-
vatKeeper.deleteCListEntry(kpid, vpid);
402+
deleteCListEntryIfEasy(kpid, vpid, kp.data);
391403
await doProcess(
392404
['notifyFulfillToData', vpid, vatData],
393405
`vat[${vatID}].promise[${vpid}] fulfillToData failed`,
394406
);
395-
vatKeeper.deleteCListEntry(kpid, vpid);
396407
} else if (kp.state === 'rejected') {
397408
const vpid = mapKernelSlotToVatSlot(kpid);
398409
const vatData = harden({
399410
...kp.data,
400411
slots: kp.data.slots.map(slot => mapKernelSlotToVatSlot(slot)),
401412
});
402-
vatKeeper.deleteCListEntry(kpid, vpid);
413+
deleteCListEntryIfEasy(kpid, vpid, kp.data);
403414
await doProcess(
404415
['notifyReject', vpid, vatData],
405416
`vat[${vatID}].promise[${vpid}] reject failed`,
406417
);
407-
vatKeeper.deleteCListEntry(kpid, vpid);
408418
} else {
409419
throw new Error(`unknown kernelPromise state '${kp.state}'`);
410420
}

packages/SwingSet/test/test-vpid-liveslots.js

+10-12
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,10 @@ async function doVatResolveCase1(t, mode) {
243243
type: 'send',
244244
targetSlot: target1,
245245
method: 'two',
246-
args: capargs([slot0arg], [expectedP3]),
247-
resultSlot: expectedP4,
246+
args: capargs([slot0arg], [expectedP1]),
247+
resultSlot: expectedP3,
248248
});
249-
t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP4 });
250-
t.deepEqual(log.shift(), resolutionOf(expectedP3, mode, target2));
249+
t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP3 });
251250
t.deepEqual(log, []);
252251

253252
t.end();
@@ -653,28 +652,27 @@ async function doVatResolveCase4(t, mode) {
653652
await endOfCrank();
654653

655654
const expectedP4 = nextP();
656-
const expectedP5 = nextP();
657655
t.deepEqual(log.shift(), {
658656
type: 'send',
659657
targetSlot: target1,
660658
method: 'three',
661-
args: capargs([slot0arg], [expectedP4]),
662-
resultSlot: expectedP5,
659+
args: capargs([slot0arg], [p1]),
660+
resultSlot: expectedP4,
663661
});
664-
t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP5 });
662+
t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP4 });
665663

666664
if (mode === 'presence') {
667665
const expectedP5 = nextP();
668666
t.deepEqual(log.shift(), {
669667
type: 'send',
670-
targetSlot: target2,
668+
targetSlot: target2, // this depends on #823 being fixed
671669
method: 'four',
672670
args: capargs([], []),
673-
resultSlot: expectedP6,
671+
resultSlot: expectedP5,
674672
});
675-
t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP6 });
673+
t.deepEqual(log.shift(), { type: 'subscribe', target: expectedP5 });
676674
}
677-
t.deepEqual(log.shift(), resolutionOf(expectedP4, mode, target2));
675+
// if p1 rejects or resolves to data, the kernel never hears about four()
678676
t.deepEqual(log, []);
679677

680678
t.end();

0 commit comments

Comments
 (0)