Skip to content

Commit 7a2c830

Browse files
committed
feat(TimerService): add new delay method and protect device args
1 parent fd44ce7 commit 7a2c830

File tree

4 files changed

+80
-33
lines changed

4 files changed

+80
-33
lines changed

packages/SwingSet/src/devices/timer-src.js

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
/**
22
* A Timer device that provides a capability that can be used to schedule wake()
33
* calls at particular times. The simpler form is a handler object with a wake()
4-
* function can be passed to D(timer).setWakeup(delaySecs, handler) to be woken
5-
* after delaySecs.
4+
* function can be passed to D(timer).setWakeup(baseTime, handler) to be woken
5+
* after baseTime.
66
*
7-
* The other form r = D(timer).makeRepeater(startTime, interval) allows one to
7+
* The other form r = D(timer).makeRepeater(baseTime, interval) allows one to
88
* create a repeater object which can be used to scheduled regular wakeups. Each
99
* time D(timer).schedule(r, w) is called, w.wake(r) will be scheduled to be
1010
* called after the next multiple of interval since startTime. This doesn't
@@ -265,11 +265,11 @@ export function buildRootDeviceNode(tools) {
265265
// but the repeated calls won't accumulate timing drift, so the trigger
266266
// point will be reached at consistent intervals.
267267
return Far('root', {
268-
setWakeup(delaySecs, handler) {
269-
assert.typeof(delaySecs, 'bigint');
270-
deadlines.add(lastPolled + Nat(delaySecs), handler);
268+
setWakeup(baseTime, handler) {
269+
baseTime = Nat(baseTime);
270+
deadlines.add(baseTime, handler);
271271
saveState();
272-
return lastPolled + Nat(delaySecs);
272+
return baseTime;
273273
},
274274
removeWakeup(handler) {
275275
const times = deadlines.remove(handler);

packages/SwingSet/src/vats/types.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,19 @@
1010
* `repeater.schedule(h)`.
1111
* @property {(delay: RelativeTime, interval: RelativeTime) => TimerRepeater} createRepeater
1212
* DEPRECATED: use makeRepeater instead.
13-
* @property {(delaySecs: RelativeTime, interval: RelativeTime) => TimerRepeater} makeRepeater
13+
* @property {(delay: RelativeTime, interval: RelativeTime) => TimerRepeater} makeRepeater
1414
* Create and return a repeater that will schedule `wake()` calls
1515
* repeatedly at times that are a multiple of interval following delay.
1616
* Interval is the difference between successive times at which wake will be
1717
* called. When `schedule(w)` is called, `w.wake()` will be scheduled to be
1818
* called after the next multiple of interval from the base. Since times can be
1919
* coarse-grained, the actual call may occur later, but this won't change when
2020
* the next event will be called.
21-
* @property {(delaySecs: RelativeTime, interval: RelativeTime) => Notifier<Timestamp>} makeNotifier
21+
* @property {(delay: RelativeTime, interval: RelativeTime) => Notifier<Timestamp>} makeNotifier
2222
* Create and return a Notifier that will deliver updates repeatedly at times
2323
* that are a multiple of interval following delay.
24+
* @property {(delay: RelativeTime) => Promise<Timestamp>} delay
25+
* Create and return a promise that will resolve after the relative time has passed.
2426
*/
2527

2628
/**

packages/SwingSet/src/vats/vat-timerWrapper.js

+41-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
1+
// @ts-check
2+
13
import { Nat } from '@agoric/nat';
24
import { assert, details as X } from '@agoric/assert';
35
import { Far } from '@agoric/marshal';
46
import { makeNotifierKit } from '@agoric/notifier';
7+
import { makePromiseKit } from '@agoric/promise-kit';
58

69
export function buildRootObject(vatPowers) {
710
const { D } = vatPowers;
@@ -13,26 +16,30 @@ export function buildRootObject(vatPowers) {
1316
getCurrentTimestamp() {
1417
return Nat(D(timerNode).getLastPolled());
1518
},
16-
setWakeup(delaySecs, handler) {
17-
return D(timerNode).setWakeup(delaySecs, handler);
19+
setWakeup(baseTime, handler) {
20+
baseTime = Nat(baseTime);
21+
return D(timerNode).setWakeup(baseTime, handler);
1822
},
1923
// can be used after setWakeup(h) or schedule(h)
2024
removeWakeup(handler) {
2125
return D(timerNode).removeWakeup(handler);
2226
},
2327
// deprecated in favor of makeRepeater().
2428
// TODO(#2164): remove before Beta
25-
createRepeater(delaySecs, interval) {
26-
return timerService.makeRepeater(delaySecs, interval);
29+
createRepeater(delay, interval) {
30+
delay = Nat(delay);
31+
interval = Nat(interval);
32+
return timerService.makeRepeater(delay, interval);
2733
},
28-
makeRepeater(delaySecs, interval) {
29-
Nat(delaySecs);
34+
makeRepeater(delay, interval) {
35+
delay = Nat(delay);
36+
interval = Nat(interval);
3037
assert(
31-
Nat(interval) > 0,
38+
interval > 0,
3239
X`makeRepeater's second parameter must be a positive integer: ${interval}`,
3340
);
3441

35-
const index = D(timerNode).makeRepeater(delaySecs, interval);
42+
const index = D(timerNode).makeRepeater(delay, interval);
3643

3744
const vatRepeater = Far('vatRepeater', {
3845
schedule(h) {
@@ -46,22 +53,44 @@ export function buildRootObject(vatPowers) {
4653
repeaters.set(index, vatRepeater);
4754
return vatRepeater;
4855
},
49-
makeNotifier(delaySecs, interval) {
50-
Nat(delaySecs);
56+
makeNotifier(delay, interval) {
57+
delay = Nat(delay);
58+
interval = Nat(interval);
5159
assert(
52-
Nat(interval) > 0,
60+
interval > 0,
5361
X`makeNotifier's second parameter must be a positive integer: ${interval}`,
5462
);
5563

56-
const index = D(timerNode).makeRepeater(delaySecs, interval);
64+
const index = D(timerNode).makeRepeater(delay, interval);
5765
const { notifier, updater } = makeNotifierKit();
5866
const updateHandler = Far('updateHandler', {
5967
wake: updater.updateState,
6068
});
6169
D(timerNode).schedule(index, updateHandler);
6270

71+
// FIXME: The fact that we never delete the repeater (for the `index`)
72+
// means that there is a resource leak and no way the repeater ever
73+
// stops.
74+
//
75+
// This happens even if every recipient of the notifier permanently
76+
// stops asking for updates, or equivalently, they drop all references
77+
// to the notifier.
78+
//
79+
// To solve this problem, we could elegantly use something like #3854.
80+
6381
return notifier;
6482
},
83+
delay(delay) {
84+
delay = Nat(delay);
85+
const now = timerService.getCurrentTimestamp();
86+
const baseTime = now + delay;
87+
const promiseKit = makePromiseKit();
88+
const delayHandler = Far('delayHandler', {
89+
wake: promiseKit.resolve,
90+
});
91+
timerService.setWakeup(baseTime, delayHandler);
92+
return promiseKit.promise;
93+
},
6594
});
6695
return timerService;
6796
}

packages/zoe/tools/manualTimer.js

+28-12
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Far } from '@agoric/marshal';
99
import './types.js';
1010
import './internal-types.js';
1111
import { makeNotifierKit } from '@agoric/notifier';
12+
import { makePromiseKit } from '@agoric/promise-kit';
1213

1314
/**
1415
* A fake clock that also logs progress.
@@ -25,11 +26,11 @@ export default function buildManualTimer(log, startValue = 0n, timeStep = 1n) {
2526
// Legacy because the value is mutated after it is stored.
2627
const schedule = makeLegacyMap('Timestamp');
2728

28-
const makeRepeater = (delaySecs, interval, timer) => {
29-
assert.typeof(delaySecs, 'bigint');
29+
const makeRepeater = (delay, interval, timer) => {
30+
assert.typeof(delay, 'bigint');
3031
assert(
31-
delaySecs % timeStep === 0n,
32-
`timer has a resolution of ${timeStep}; ${delaySecs} is not divisible`,
32+
delay % timeStep === 0n,
33+
`timer has a resolution of ${timeStep}; ${delay} is not divisible`,
3334
);
3435
assert.typeof(interval, 'bigint');
3536
assert(
@@ -70,7 +71,7 @@ export default function buildManualTimer(log, startValue = 0n, timeStep = 1n) {
7071
timer.removeWakeup(repeaterWaker);
7172
},
7273
});
73-
nextWakeup = ticks + delaySecs;
74+
nextWakeup = ticks + delay;
7475
timer.setWakeup(nextWakeup, repeaterWaker);
7576
return repeater;
7677
};
@@ -135,14 +136,14 @@ export default function buildManualTimer(log, startValue = 0n, timeStep = 1n) {
135136
createRepeater(delay, interval) {
136137
return makeRepeater(delay, interval, timer);
137138
},
138-
makeRepeater(delaySecs, interval) {
139-
return makeRepeater(delaySecs, interval, timer);
139+
makeRepeater(delay, interval) {
140+
return makeRepeater(delay, interval, timer);
140141
},
141-
makeNotifier(delaySecs, interval) {
142-
assert.typeof(delaySecs, 'bigint');
142+
makeNotifier(delay, interval) {
143+
assert.typeof(delay, 'bigint');
143144
assert(
144-
(delaySecs % timeStep) === 0n,
145-
`timer has a resolution of ${timeStep}; ${delaySecs} is not divisible`,
145+
(delay % timeStep) === 0n,
146+
`timer has a resolution of ${timeStep}; ${delay} is not divisible`,
146147
);
147148
assert.typeof(interval, 'bigint');
148149
assert(
@@ -158,9 +159,24 @@ export default function buildManualTimer(log, startValue = 0n, timeStep = 1n) {
158159
timer.setWakeup(ticks + interval, repeaterWaker);
159160
},
160161
});
161-
timer.setWakeup(ticks + delaySecs, repeaterWaker);
162+
timer.setWakeup(ticks + delay, repeaterWaker);
162163
return notifier;
163164
},
165+
delay(delay) {
166+
assert.typeof(delay, 'bigint');
167+
assert(
168+
(delay % timeStep) === 0n,
169+
`timer has a resolution of ${timeStep}; ${delay} is not divisible`,
170+
);
171+
const promiseKit = makePromiseKit();
172+
const delayWaker = Far('delayWaker', {
173+
wake(timestamp) {
174+
promiseKit.resolve(timestamp);
175+
},
176+
});
177+
timer.setWakeup(delay, delayWaker);
178+
return promiseKit.promise;
179+
},
164180
});
165181
return timer;
166182
}

0 commit comments

Comments
 (0)