Skip to content

Commit 0d42e64

Browse files
committed
fix: add tests and correct issues the tests found
1 parent 6793925 commit 0d42e64

File tree

4 files changed

+465
-17
lines changed

4 files changed

+465
-17
lines changed

packages/SwingSet/src/vats/comms/clist-kernel.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export function makeKernel(state, syscall, stateKit) {
6363

6464
function provideKernelForLocalResult(lpid) {
6565
if (!lpid) {
66-
return null;
66+
return undefined;
6767
}
6868
const p = state.promiseTable.get(lpid);
6969
assert(!p.resolved, X`result ${lpid} is already resolved`);

packages/SwingSet/src/vats/comms/delivery.js

+24
Original file line numberDiff line numberDiff line change
@@ -430,15 +430,31 @@ export function makeDeliveryKit(state, syscall, transmit, clistKit, stateKit) {
430430
}
431431

432432
function handleResolutions(resolutions) {
433+
// All promises listed as *targets* in `resolutions` are by definition
434+
// unresolved when they arrive here: they are being decided by someone else
435+
// (the kernel, or a remote). They are either primary (previously known to
436+
// us) or auxilliary (imported, in an unresolved state, as the resolution
437+
// data of the batch was translated from whatever kernel/remote sent the
438+
// batch into our local namespace). The resolution data in `resolutions`
439+
// may point to both resolved and unresolved promises, but any cycles
440+
// (pointers into other targets of `resolutions`) will still point to
441+
// unresolved promises, because we haven't marked them as resolved quite
442+
// yet.
433443
const [[primaryLpid]] = resolutions;
434444
const { subscribers, kernelIsSubscribed } = getPromiseSubscribers(
435445
primaryLpid,
436446
);
447+
448+
// Run the collector *before* we change the state of our promise table.
437449
const collector = resolutionCollector();
438450
for (const resolution of resolutions) {
439451
const [_lpid, _rejected, data] = resolution;
440452
collector.forSlots(data.slots);
441453
}
454+
455+
// The collector now knows about every previously-resolved promise cited by
456+
// the resolution data of `resolutions`. This will never overlap with the
457+
// targets of `resolutions`.
442458
for (const resolution of resolutions) {
443459
const [lpid, rejected, data] = resolution;
444460
// rejected: boolean, data: capdata
@@ -451,9 +467,13 @@ export function makeDeliveryKit(state, syscall, transmit, clistKit, stateKit) {
451467
// be handled properly
452468
markPromiseAsResolved(lpid, rejected, data);
453469
}
470+
// At this point, both the primary and auxillary promises listed as
471+
// targets in `resolutions` are marked as resolved.
472+
454473
const auxResolutions = collector.getResolutions();
455474
if (auxResolutions.length > 0) {
456475
// console.log(`@@@ adding ${auxResolutions.length} aux resolutions`);
476+
// Concat because `auxResolutions` and `resolutions` are strictly disjoint
457477
resolutions = resolutions.concat(auxResolutions);
458478
}
459479

@@ -476,6 +496,7 @@ export function makeDeliveryKit(state, syscall, transmit, clistKit, stateKit) {
476496

477497
function resolveToRemote(remoteID, resolutions) {
478498
const msgs = [];
499+
const retires = [];
479500
for (const resolution of resolutions) {
480501
const [lpid, rejected, data] = resolution;
481502

@@ -495,6 +516,9 @@ export function makeDeliveryKit(state, syscall, transmit, clistKit, stateKit) {
495516
const rejectedTag = rejected ? 'reject' : 'fulfill';
496517
// prettier-ignore
497518
msgs.push(`resolve:${rejectedTag}:${rpid}${mapSlots()};${data.body}`);
519+
retires.push(rpid);
520+
}
521+
for (const rpid of retires) {
498522
beginRemotePromiseIDRetirement(remoteID, rpid);
499523
}
500524
transmit(remoteID, msgs.join('\n'));

packages/SwingSet/test/commsVatDriver.js

+31-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { assert, details as X } from '@agoric/assert';
22
import buildCommsDispatch from '../src/vats/comms';
3+
import { debugState } from '../src/vats/comms/dispatch';
34
import { flipRemoteSlot } from '../src/vats/comms/parseRemoteSlot';
45

56
// This module provides a power tool for testing the comms vat implementation.
@@ -58,7 +59,7 @@ import { flipRemoteSlot } from '../src/vats/comms/parseRemoteSlot';
5859
//
5960
// WHAT is a string of the form `${who}${dir}${op}`
6061
// ${who} is the actor: 'k' (kernel) or 'a', 'b', or 'c' (remotes)
61-
// ${dir} is the direction: '>' (inject) or '<' (observe)
62+
// ${dir} is the direction: '>' (inject), '<' (observe), or ':' (control)
6263
// ${op} is the operation: 'm' (message), 'r' (resolve), 's' (subscribe), or 'l' (lag)
6364
//
6465
// OTHERSTUFF depends on ${op}:
@@ -85,7 +86,7 @@ import { flipRemoteSlot } from '../src/vats/comms/parseRemoteSlot';
8586
// acknowledged DELAY deliveries (messages or resolutions) behind messages
8687
// sent to the remote. The lag can be reduced to a lower value. It can
8788
// also be turned off again with a DELAY of 0.
88-
// Lag is only allowed if ${who} is a remote and ${dir} is '>'
89+
// Lag is only allowed if ${who} is a remote and ${dir} is ':'
8990
//
9091
// Any promise or object reference called for the in API above should be given
9192
// as a string of the form `@REF` or `@REF:IFACE`. REF is a normal vat vref or
@@ -114,6 +115,10 @@ import { flipRemoteSlot } from '../src/vats/comms/parseRemoteSlot';
114115
// describe. It will be a test failure if these do not match or if the log
115116
// array is empty.
116117
//
118+
// A ':' (control) operation will alter the behavior of the simulation conducted
119+
// by the vat driver framework. Currently the only control operation is 'l',
120+
// which manipulates the lag of remotes.
121+
//
117122
// The `done()` function should be called at the end of the test, and will fail
118123
// the test if the log array at that point is not empty.
119124
//
@@ -168,7 +173,7 @@ function loggingSyscall(log) {
168173
*
169174
* @returns {string} the ref embedded within `scriptRef`
170175
*/
171-
function refFromScriptRef(scriptRef) {
176+
function refOf(scriptRef) {
172177
assert(
173178
scriptRef[0] === '@',
174179
X`expected reference ${scriptRef} to start with '@'`,
@@ -183,6 +188,10 @@ function refFromScriptRef(scriptRef) {
183188
return ref;
184189
}
185190

191+
function flipRefOf(scriptRef) {
192+
return flipRemoteSlot(refOf(scriptRef));
193+
}
194+
186195
/**
187196
* Extract the interface name embedded in a reference string as found in a test
188197
* script. This will be a string of the form `@${ref}` or `@${ref}:${iface}`.
@@ -193,7 +202,7 @@ function refFromScriptRef(scriptRef) {
193202
*
194203
* @returns {string|undefined} the ref embedded within `scriptRef`
195204
*/
196-
function ifaceFromScriptRef(scriptRef) {
205+
function ifaceOf(scriptRef) {
197206
const delim = scriptRef.indexOf(':');
198207
if (delim < 0) {
199208
return undefined;
@@ -227,13 +236,13 @@ function capdata(from) {
227236
}
228237
case 'string':
229238
if (value[0] === '@') {
230-
const ref = refFromScriptRef(value);
239+
const ref = refOf(value);
231240
let index = slots.findIndex(x => x === ref);
232241
if (index < 0) {
233242
index = slots.length;
234243
slots.push(ref);
235244
}
236-
const iface = ifaceFromScriptRef(value);
245+
const iface = ifaceOf(value);
237246
return { [QCLASS]: 'slot', iface, index };
238247
} else {
239248
return value;
@@ -330,6 +339,7 @@ export function commsVatDriver(t, verbose = false) {
330339
const log = [];
331340
const syscall = loggingSyscall(log);
332341
const d = buildCommsDispatch(syscall, 'fakestate', 'fakehelpers');
342+
const { state } = debugState.get(d);
333343

334344
const remotes = new Map();
335345

@@ -534,8 +544,8 @@ export function commsVatDriver(t, verbose = false) {
534544
*/
535545
function makeNewRemote(name, transmitter, receiver) {
536546
remotes.set(name, {
537-
transmitter: refFromScriptRef(transmitter),
538-
receiver: refFromScriptRef(receiver),
547+
transmitter: refOf(transmitter),
548+
receiver: refOf(receiver),
539549
sendToSeqNum: 1,
540550
sendFromSeqNum: 1,
541551
lastToSeqNum: 0,
@@ -619,7 +629,7 @@ export function commsVatDriver(t, verbose = false) {
619629
}
620630
}
621631

622-
const exportObjectCounter = { k: 30, a: 20, b: 20, c: 20 };
632+
const exportObjectCounter = { k: 30, a: 20, b: 1020, c: 2020 };
623633
/**
624634
* Allocate a new scriptref for an exported object.
625635
*
@@ -657,14 +667,15 @@ export function commsVatDriver(t, verbose = false) {
657667
}
658668
const [who, dir, op] = what;
659669
insistProperActor(who);
660-
assert(dir === '>' || dir === '<');
670+
assert(dir === '>' || dir === '<' || dir === ':');
661671
assert(op === 'm' || op === 'r' || op === 's' || op === 'l');
662672

663673
switch (op) {
664674
case 'm': {
665-
const target = refFromScriptRef(params[0]);
675+
assert(dir === '<' || dir === '>');
676+
const target = refOf(params[0]);
666677
const method = params[1];
667-
const result = params[2] ? refFromScriptRef(params[2]) : undefined;
678+
const result = params[2] ? refOf(params[2]) : undefined;
668679
const args = capdata(params.slice(3));
669680
if (dir === '>') {
670681
injectSend(who, target, method, args, result);
@@ -674,9 +685,10 @@ export function commsVatDriver(t, verbose = false) {
674685
break;
675686
}
676687
case 'r': {
688+
assert(dir === '<' || dir === '>');
677689
const resolutions = [];
678690
for (const resolution of params) {
679-
const target = refFromScriptRef(resolution[0]);
691+
const target = refOf(resolution[0]);
680692
const status = resolution[1];
681693
const value = capdata(resolution[2]);
682694
resolutions.push([target, status, value]);
@@ -691,13 +703,13 @@ export function commsVatDriver(t, verbose = false) {
691703
case 's': {
692704
// The 's' (subscribe) op is only allowed as a kernal observation
693705
assert(who === 'k' && dir === '<');
694-
const target = refFromScriptRef(params[0]);
706+
const target = refOf(params[0]);
695707
observeSubscribe(target);
696708
break;
697709
}
698710
case 'l': {
699-
// The 'l' (lag) op is only allowed as a remote injection
700-
assert(who !== 'k' && dir === '>');
711+
// The 'l' (lag) op is only allowed as a control operation
712+
assert(who !== 'k' && dir === ':');
701713
injectLag(who, params[0]);
702714
break;
703715
}
@@ -769,6 +781,7 @@ export function commsVatDriver(t, verbose = false) {
769781

770782
return {
771783
_,
784+
state,
772785
done,
773786
setupRemote,
774787
importFromRemote,
@@ -777,5 +790,7 @@ export function commsVatDriver(t, verbose = false) {
777790
newExportObject,
778791
newImportPromise,
779792
newExportPromise,
793+
refOf,
794+
flipRefOf,
780795
};
781796
}

0 commit comments

Comments
 (0)