|
2 | 2 |
|
3 | 3 | There's documentation elsewhere about [how devices fit into the SwingSet
|
4 | 4 | architecture](devices.md). In order to install a Timer device, you first build
|
5 |
| -a timer object in order to create the timer's endowments, source code, and |
| 5 | +a timer object in order to create the timer's endowments, source code, and |
6 | 6 | `poll()` function.
|
7 | 7 |
|
8 | 8 | ## Kernel Configuration
|
9 | 9 |
|
10 | 10 | The timer service consists of a device (`device-timer`) and a helper vat (`vat-timer`). The host application must configure the device as it builds the swingset kernel, and then the bootstrap vat must finish the job by wiring the device and vat together.
|
11 | 11 |
|
12 |
| -``` |
| 12 | +```js |
13 | 13 | import { buildTimer } from `@agoric/swingset-vat`;
|
14 | 14 | const timer = buildTimer();
|
15 | 15 | ```
|
@@ -67,42 +67,86 @@ A single application might have multiple sources of time, which would require th
|
67 | 67 | The `timerService` object can be distributed to other vats as necessary.
|
68 | 68 |
|
69 | 69 | ```js
|
70 |
| - // for this example, assume poll() provides seconds-since-epoch as a BigInt |
| 70 | + // for this example, assume poll() provides seconds-since-epoch |
71 | 71 |
|
72 | 72 | const now = await E(timerService).getCurrentTimestamp();
|
73 |
| - |
74 |
| - // simple non-cancellable Promise-based delay |
75 |
| - const p = E(timerService).delay(30); // fires 30 seconds from now |
76 |
| - await p; |
77 | 73 |
|
78 |
| - // to cancel wakeups, first build a handler |
| 74 | + // simple one-shot Promise-based relative delay |
| 75 | + const p1 = E(timerService).delay(30n); // fires 30 seconds from now |
| 76 | + await p1; |
| 77 | + |
| 78 | + // same, but cancellable |
| 79 | + const cancel2 = Far('cancel', {}); // any pass-by-reference object |
| 80 | + // the cancelToken is always optional |
| 81 | + const p2 = E(timerService).delay(30n, cancel2); |
| 82 | + // E(timerService).cancel(cancel2) will cancel that |
| 83 | + |
| 84 | + // same, but absolute instead of relative-to-now |
| 85 | + const monday = 1_660_000_000; |
| 86 | + const p3 = E(timerService).wakeAt(monday, cancel2); |
| 87 | + await p3; // fires Mon Aug 8 16:06:40 2022 PDT |
79 | 88 |
|
| 89 | + // non-Promise API functions needs a handler callback |
80 | 90 | const handler = Far('handler', {
|
81 |
| - wake(t) { console.log(`woken up at ${t}`); }, |
| 91 | + wake(t) { console.log(`woken up, scheduled for ${t}`); }, |
82 | 92 | });
|
83 |
| - |
84 |
| - // then for one-shot wakeups: |
85 |
| - await E(timerService).setWakeup(startTime, handler); |
86 |
| - // handler.wake(t) will be called shortly after 'startTime' |
| 93 | + |
| 94 | + // then for one-shot absolute wakeups: |
| 95 | + await E(timerService).setWakeup(monday, handler, cancel2); |
| 96 | + // handler.wake(t) will be called shortly after monday |
87 | 97 |
|
88 | 98 | // cancel early:
|
89 |
| - await E(timerService).removeWakeup(handler); |
| 99 | + await E(timerService).cancel(cancel2); |
90 | 100 |
|
91 | 101 | // wake up at least 60 seconds from now:
|
92 |
| - await E(timerService).setWakeup(now + 60n, handler); |
93 |
| - |
| 102 | + await E(timerService).setWakeup(now + 60n, handler, cancel2); |
94 | 103 |
|
95 |
| - // makeRepeater() creates a repeating wakeup service: the handler will |
| 104 | + // repeatAfter() creates a repeating wakeup service: the handler will |
96 | 105 | // fire somewhat after 80 seconds from now (delay+interval), and again
|
97 |
| - // every 60 seconds thereafter. Individual wakeups might be delayed, |
98 |
| - // but the repeater will not accumulate drift. |
| 106 | + // every 60 seconds thereafter. The next wakeup will not be scheduled |
| 107 | + // until the handler message is acknowledged (when its return promise is |
| 108 | + // fulfilled), so wakeups might be skipped, but they will always be |
| 109 | + // scheduled for the next 'now + delay + k * repeat', so they will not |
| 110 | + // accumulate drift. If the handler rejects, the repeater will be |
| 111 | + // cancelled. |
99 | 112 |
|
100 | 113 | const delay = 20n;
|
101 | 114 | const interval = 60n;
|
| 115 | + E(timerService).repeatAfter(delay, interval, handler, cancel2); |
| 116 | + |
| 117 | + // repeating wakeup service, Notifier-style . This supports both the |
| 118 | + // native 'E(notifierP).getUpdateSince()' Notifier protocol, and an |
| 119 | + // asyncIterator. To use it in a for/await loop (which does not know how |
| 120 | + // to make `E()`-style eventual sends to the remote notifier), you must |
| 121 | + // wrap it in a local "front-end" Notifier by calling `makeNotifier()`. |
| 122 | + |
| 123 | + const notifierP = E(timerService).makeNotifier(delay, interval, cancel2); |
| 124 | + // import { makeNotifier } from '@agoric/notifier'; |
| 125 | + const notifier = makeNotifier(notifierP); |
| 126 | + |
| 127 | + for await (const scheduled of notifier) { |
| 128 | + console.log(`woken up, scheduled for ${scheduled}`); |
| 129 | + // note: runs forever, once per 'interval' |
| 130 | + break; // unless you escape early |
| 131 | + } |
| 132 | + |
| 133 | + // `makeRepeater` creates a "repeater object" with .schedule |
| 134 | + // and .disable methods to turn it on and off |
| 135 | + |
102 | 136 | const r = E(timerService).makeRepeater(delay, interval);
|
103 | 137 | E(r).schedule(handler);
|
104 | 138 | E(r).disable(); // cancel and delete entire repeater
|
105 |
| - |
106 |
| - // repeating wakeup service, Notifier-style |
107 |
| - const notifier = E(timerService).makeNotifier(delay, interval); |
| 139 | + |
| 140 | + // the 'clock' facet offers `getCurrentTimestamp` and nothing else |
| 141 | + const clock = await E(timerService).getClock(); |
| 142 | + const now2 = await E(clock).getCurrentTimestamp(); |
| 143 | + |
| 144 | + // a "Timer Brand" is an object that identifies the source of time |
| 145 | + // used by any given TimerService, without exposing any authority |
| 146 | + // to get the time or schedule wakeups |
| 147 | + |
| 148 | + const brand1 = await E(timerService).getTimerBrand(); |
| 149 | + const brand2 = await E(clock).getTimerBrand(); |
| 150 | + assert.equal(brand1, brand2); |
| 151 | + assert(await E(brand1).isMyTimerService(timerService)); |
108 | 152 | ```
|
0 commit comments