-
Notifications
You must be signed in to change notification settings - Fork 206
/
kernelQueue.js
124 lines (116 loc) · 4.11 KB
/
kernelQueue.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import { kser } from '@agoric/kmarshal';
import { insistKernelType, parseKernelSlot } from './parseKernelSlots.js';
import { insistCapData } from '../lib/capdata.js';
import { insistMessage } from '../lib/message.js';
import { insistVatID } from '../lib/id.js';
/**
* @param {object} tools
* @param {KernelKeeper} tools.kernelKeeper Kernel keeper managing persistent kernel state
* @param {(problem: unknown, err?: Error) => void } [tools.panic]
*/
export function makeKernelQueueHandler(tools) {
const {
kernelKeeper,
panic = (problem, err) => {
throw err || Error(`kernel panic ${problem}`);
},
} = tools;
function notify(vatID, kpid) {
const m = harden({ type: 'notify', vatID, kpid });
kernelKeeper.incrementRefCount(kpid, `enq|notify`);
kernelKeeper.addToAcceptanceQueue(m);
}
function doSubscribe(vatID, kpid) {
insistVatID(vatID);
const p = kernelKeeper.getKernelPromise(kpid);
if (p.state === 'unresolved') {
kernelKeeper.addSubscriberToPromise(kpid, vatID);
} else {
// otherwise it's already resolved, you probably want to know how
notify(vatID, kpid);
}
}
function doResolve(vatID, resolutions) {
if (vatID) {
insistVatID(vatID);
}
for (const resolution of resolutions) {
const [kpid, rejected, data] = resolution;
insistKernelType('promise', kpid);
insistCapData(data);
const p = kernelKeeper.getResolveablePromise(kpid, vatID);
const { subscribers } = p;
for (const subscriber of subscribers) {
notify(subscriber, kpid);
}
kernelKeeper.resolveKernelPromise(kpid, rejected, data);
const tag = rejected ? 'rejected' : 'fulfilled';
if (p.policy === 'logAlways' || (rejected && p.policy === 'logFailure')) {
console.log(
`${kpid}.policy ${p.policy}: ${tag} ${JSON.stringify(data)}`,
);
} else if (rejected && p.policy === 'panic') {
panic(`${kpid}.policy panic: ${tag} ${JSON.stringify(data)}`);
}
}
}
function resolveToError(kpid, errorData, expectedDecider) {
doResolve(expectedDecider, [[kpid, true, errorData]]);
}
function doSend(target, msg) {
parseKernelSlot(target);
insistMessage(msg);
const m = harden({ type: 'send', target, msg });
kernelKeeper.incrementRefCount(target, `enq|msg|t`);
if (msg.result) {
kernelKeeper.incrementRefCount(msg.result, `enq|msg|r`);
}
let idx = 0;
for (const argSlot of msg.methargs.slots) {
kernelKeeper.incrementRefCount(argSlot, `enq|msg|s${idx}`);
idx += 1;
}
kernelKeeper.addToAcceptanceQueue(m);
}
/**
* Enqueue a message to some kernel object, as if the message had been sent
* by some other vat. This requires a kref as a target.
*
* @param {string} kref Target of the message
* @param {string|symbol} method The method name
* @param {any[]} args The arguments array
* @param {ResolutionPolicy} [policy] How the kernel should handle an eventual
* resolution or rejection of the message's result promise. Should be
* one of 'none' (don't even create a result promise), 'ignore' (do
* nothing), 'logAlways' (log the resolution or rejection), 'logFailure'
* (log only rejections), or 'panic' (panic the kernel upon a
* rejection).
* @returns {string | undefined} the kpid of the sent message's result promise, if any
*/
function queueToKref(kref, method, args, policy = 'ignore') {
// queue a message on the end of the queue, with 'absolute' krefs.
// Use 'step' or 'run' to execute it
const methargs = kser([method, args]);
for (const s of methargs.slots) {
parseKernelSlot(s);
}
let resultKPID;
if (policy !== 'none') {
resultKPID = kernelKeeper.addKernelPromise(policy);
}
// Should we actually increment these stats in this case?
kernelKeeper.incStat('syscalls');
kernelKeeper.incStat('syscallSend');
const msg = harden({ methargs, result: resultKPID });
doSend(kref, msg);
return resultKPID;
}
return harden({
doSend,
doSubscribe,
doResolve,
notify,
resolveToError,
queueToKref,
});
}