Skip to content

Commit

Permalink
Merge pull request #3853 from Agoric/mfig-timer-renovation
Browse files Browse the repository at this point in the history
Renovate timer services
  • Loading branch information
michaelfig authored Sep 20, 2021
2 parents c50dc5d + 3654717 commit 99d66bf
Show file tree
Hide file tree
Showing 11 changed files with 102 additions and 211 deletions.
34 changes: 24 additions & 10 deletions packages/SwingSet/docs/timer.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,29 +33,43 @@ and creating the service. The vat is responsible for creating repeater objects
to wrap the device node as a capability that can be accessed by regular vat
code.

In `bootstrap()` for a particular SwingSet, we create a timerService, and
make it accessible to the user in `home`
In `bootstrap()` for a particular SwingSet, we create a timerService, and make
it accessible to the user in `home.localTimerService` (whose timestamps are
measured in milliseconds).

```
const timerService = E(vats.timerWrapper).createTimerService(devices.timer);
```

Then users in the REPL can use the timerService to schedule wakeups.
Then users in the REPL can use the `localTimerService` (in milliseconds since
the epoch) or `chainTimerService` (in seconds since the epoch) to schedule wakeups.

There is a simple promise-based `delay` function that resolves its returned promise
when at least its first `delay` argument has passed.

```js
E(home.localTimerService).delay(60_000n);
```
const timestampP = E(timerService).getCurrentTimestamp();

const handler = harden({wake(now) { console.log(`woke up ${now}`); }});
const willWakeAt = E(timerService).setWakeup(60, handler);
This promise will resolve to the current timestamp after 60 seconds from now.

Alteratively, if you wish to be able to cancel the delay, you can use the more
powerful `setWakeup` method:

```js
timestamp = E(home.localTimerService).getCurrentTimestamp();

handler = Far('waker', { wake(now) { console.log(`woke up ${now}`); } });
willWakeAt = E(home.localTimerService).setWakeup(timestamp + 60_000n, handler);
```

The handler will fire somewhat after 60 seconds from now.

```
const repeater = E(timerService).makeRepeater(20, 60);
```js
repeater = E(home.localTimerService).makeRepeater(20_000n, 60_000n);
E(repeater).schedule(handler);
```

The handler will fire in somewhat after 80 seconds from now, and every 60
seconds thereafter. Calling `E(repeater).disable()` will cancel prevent the repeater
from scheduling future activations
seconds thereafter. Calling `E(repeater).disable()` will cancel the repeater and
prevent it from scheduling future activations
14 changes: 7 additions & 7 deletions packages/SwingSet/src/devices/timer-src.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
/**
* A Timer device that provides a capability that can be used to schedule wake()
* calls at particular times. The simpler form is a handler object with a wake()
* function can be passed to D(timer).setWakeup(delaySecs, handler) to be woken
* after delaySecs.
* function can be passed to D(timer).setWakeup(baseTime, handler) to be woken
* after baseTime.
*
* The other form r = D(timer).makeRepeater(startTime, interval) allows one to
* The other form r = D(timer).makeRepeater(baseTime, interval) allows one to
* create a repeater object which can be used to scheduled regular wakeups. Each
* time D(timer).schedule(r, w) is called, w.wake(r) will be scheduled to be
* called after the next multiple of interval since startTime. This doesn't
Expand Down Expand Up @@ -265,11 +265,11 @@ export function buildRootDeviceNode(tools) {
// but the repeated calls won't accumulate timing drift, so the trigger
// point will be reached at consistent intervals.
return Far('root', {
setWakeup(delaySecs, handler) {
assert.typeof(delaySecs, 'bigint');
deadlines.add(lastPolled + Nat(delaySecs), handler);
setWakeup(baseTime, handler) {
baseTime = Nat(baseTime);
deadlines.add(baseTime, handler);
saveState();
return lastPolled + Nat(delaySecs);
return baseTime;
},
removeWakeup(handler) {
const times = deadlines.remove(handler);
Expand Down
8 changes: 4 additions & 4 deletions packages/SwingSet/src/vats/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,19 @@
* @property {(waker: ERef<TimerWaker>) => Array<Timestamp>} removeWakeup Remove the waker
* from all its scheduled wakeups, whether produced by `timer.setWakeup(h)` or
* `repeater.schedule(h)`.
* @property {(delay: RelativeTime, interval: RelativeTime) => TimerRepeater} createRepeater
* DEPRECATED: use makeRepeater instead.
* @property {(delaySecs: RelativeTime, interval: RelativeTime) => TimerRepeater} makeRepeater
* @property {(delay: RelativeTime, interval: RelativeTime) => TimerRepeater} makeRepeater
* Create and return a repeater that will schedule `wake()` calls
* repeatedly at times that are a multiple of interval following delay.
* Interval is the difference between successive times at which wake will be
* called. When `schedule(w)` is called, `w.wake()` will be scheduled to be
* called after the next multiple of interval from the base. Since times can be
* coarse-grained, the actual call may occur later, but this won't change when
* the next event will be called.
* @property {(delaySecs: RelativeTime, interval: RelativeTime) => Notifier<Timestamp>} makeNotifier
* @property {(delay: RelativeTime, interval: RelativeTime) => Notifier<Timestamp>} makeNotifier
* Create and return a Notifier that will deliver updates repeatedly at times
* that are a multiple of interval following delay.
* @property {(delay: RelativeTime) => Promise<Timestamp>} delay
* Create and return a promise that will resolve after the relative time has passed.
*/

/**
Expand Down
52 changes: 37 additions & 15 deletions packages/SwingSet/src/vats/vat-timerWrapper.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// @ts-check

import { Nat } from '@agoric/nat';
import { assert, details as X } from '@agoric/assert';
import { Far } from '@agoric/marshal';
import { makeNotifierKit } from '@agoric/notifier';
import { makePromiseKit } from '@agoric/promise-kit';

export function buildRootObject(vatPowers) {
const { D } = vatPowers;
Expand All @@ -13,26 +16,23 @@ export function buildRootObject(vatPowers) {
getCurrentTimestamp() {
return Nat(D(timerNode).getLastPolled());
},
setWakeup(delaySecs, handler) {
return D(timerNode).setWakeup(delaySecs, handler);
setWakeup(baseTime, handler) {
baseTime = Nat(baseTime);
return D(timerNode).setWakeup(baseTime, handler);
},
// can be used after setWakeup(h) or schedule(h)
removeWakeup(handler) {
return D(timerNode).removeWakeup(handler);
},
// deprecated in favor of makeRepeater().
// TODO(#2164): remove before Beta
createRepeater(delaySecs, interval) {
return timerService.makeRepeater(delaySecs, interval);
},
makeRepeater(delaySecs, interval) {
Nat(delaySecs);
makeRepeater(delay, interval) {
delay = Nat(delay);
interval = Nat(interval);
assert(
Nat(interval) > 0,
interval > 0,
X`makeRepeater's second parameter must be a positive integer: ${interval}`,
);

const index = D(timerNode).makeRepeater(delaySecs, interval);
const index = D(timerNode).makeRepeater(delay, interval);

const vatRepeater = Far('vatRepeater', {
schedule(h) {
Expand All @@ -46,22 +46,44 @@ export function buildRootObject(vatPowers) {
repeaters.set(index, vatRepeater);
return vatRepeater;
},
makeNotifier(delaySecs, interval) {
Nat(delaySecs);
makeNotifier(delay, interval) {
delay = Nat(delay);
interval = Nat(interval);
assert(
Nat(interval) > 0,
interval > 0,
X`makeNotifier's second parameter must be a positive integer: ${interval}`,
);

const index = D(timerNode).makeRepeater(delaySecs, interval);
const index = D(timerNode).makeRepeater(delay, interval);
const { notifier, updater } = makeNotifierKit();
const updateHandler = Far('updateHandler', {
wake: updater.updateState,
});
D(timerNode).schedule(index, updateHandler);

// FIXME: The fact that we never delete the repeater (for the `index`)
// means that there is a resource leak and no way the repeater ever
// stops.
//
// This happens even if every recipient of the notifier permanently
// stops asking for updates, or equivalently, they drop all references
// to the notifier.
//
// To solve this problem, we could elegantly use something like #3854.

return notifier;
},
delay(delay) {
delay = Nat(delay);
const now = timerService.getCurrentTimestamp();
const baseTime = now + delay;
const promiseKit = makePromiseKit();
const delayHandler = Far('delayHandler', {
wake: promiseKit.resolve,
});
timerService.setWakeup(baseTime, delayHandler);
return promiseKit.promise;
},
});
return timerService;
}
Expand Down
98 changes: 0 additions & 98 deletions packages/cosmic-swingset/TimerService.md

This file was deleted.

4 changes: 1 addition & 3 deletions packages/cosmic-swingset/src/sim-chain.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,11 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
const withBlockQueue = makeWithQueue();
const unhandledSimulateBlock = withBlockQueue(
async function unqueuedSimulateBlock() {
const actualStart = Date.now();
// Gather up the new messages into the latest block.
thisBlock.push(...intoChain);
intoChain = [];

blockTime += PRETEND_BLOCK_DELAY;
blockTime = scaleBlockTime(Date.now());
blockHeight += 1;

await blockManager(
Expand Down Expand Up @@ -143,7 +142,6 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {

// We now advance to the next block.
thisBlock = [];
blockTime += scaleBlockTime(Date.now() - actualStart);

clearTimeout(nextBlockTimeout);
// eslint-disable-next-line no-use-before-define
Expand Down
2 changes: 1 addition & 1 deletion packages/solo/src/start.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ async function buildSwingset(
// other inbound messages.
const queuedMoveTimeForward = withInputQueue(
async function moveTimeForward() {
const now = Math.floor(Date.now() / intervalMillis);
const now = Date.now();
try {
if (timer.poll(now)) {
await processKernel();
Expand Down
14 changes: 0 additions & 14 deletions packages/solo/test/test-home.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,20 +166,6 @@ function makeHandler() {
});
}

// TODO(2164): createRepeater was replaced by makeRepeater. Remove it pre-Beta
test.serial('home.localTimerService createRepeater', async t => {
const { localTimerService } = E.get(home);
const timestamp = await E(localTimerService).getCurrentTimestamp();
const repeater = E(localTimerService).createRepeater(1n, 1n);
const handler = makeHandler();
await E(repeater).schedule(handler);
const notifier = E(localTimerService).makeNotifier(1n, 1n);
await E(notifier).getUpdateSince();

t.truthy(handler.getCalls() >= 1);
t.truthy(handler.getArgs()[0] > timestamp);
});

test.serial('home.localTimerService makeRepeater', async t => {
const { localTimerService } = E.get(home);
const timestamp = await E(localTimerService).getCurrentTimestamp();
Expand Down
2 changes: 1 addition & 1 deletion packages/swingset-runner/demo/zoeTests/bootstrap.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { E } from '@agoric/eventual-send';
import { Far } from '@agoric/marshal';
import { makeIssuerKit, AmountMath } from '@agoric/ertp';
import buildManualTimer from './manualTimer.js';
import buildManualTimer from '@agoric/zoe/tools/manualTimer.js';

import { makePrintLog } from './printLog.js';

Expand Down
Loading

0 comments on commit 99d66bf

Please sign in to comment.