Open MostAwesomeDude opened 8 years ago
I don't think we need to worry about unsafety of timers, so long as their outputs are loggable (I expect it'll be a while before we build a loggable-determinism version of the runtime though)
Erlang does preemptive scheduling, with context switch points embedded in every loop of every builtin function, etc. We'd need a preemptible stack for that.
Timer has safe and unsafe methods, right? That seems awkward, to me. I'd much rather have separate objects. Yes, somebody could make a facet, but it's better to be safe by default, no? That is: it's better to make the easy thing the safe thing.
I'm looking at the help... "An unsafe nondeterministic clock." er... that seems overly scary. The fromNow and sendTimestamp methods are no more unsafe than file or network I/O. And the fact the a clock is nondeterministic seems self-evident.
The unsafeNow() method lets you observe two different times in the same turn, which can screw with the whole deterministic fail-stop goal. I'd rather it were hung on a totally different object. Putting it on the currentRuntime would work for me.
Observing two different times in the same turn is fine. A system built for deterministic replay could replay the exact same timestamps.
Then why can't we have synchronous file I/O? I thought I/O had to be between turns. Surely reading the clock is I/O.
Under normal operation the clock is read-only and monotonic, whereas files are mutable.
I have no idea if this adds to the discussion or not but I am going to leave it here anyway.
Disregard the above, I was clearly too optimistic.
The crucial problem with the idea of logging timer outputs is that it only needs to be done when a turn crashes, and that's when you can't guarantee it can be logged.
There are two purposes to determinism relevant here: Accurate debugging, and inward bit confinement.
When replaying code for debugging, it's important to have the same behavior as when it ran the first time. If the vat crashed before the log could be written, there's no way to determine that with an accurate timer.
When running confined code, accurate timers could make wall-banging easier, increasing the data rate of side channels.
Therefore I think we need a safe-scope object that provides a timestamp from the start of the turn, and an unsafe-scope object that provides accurate timing.
This was an excellent IRC discussion. I agree. Of the suggestions, I like M.now()
the best. It would presumably carry a POSIX UTC timestamp, in seconds, as a Double. Or something like that.
The agoric folks are talking about a "device model" with synchronous invocation. https://github.com/Agoric/SwingSet/issues/26
seems fraught with peril, to me
Timing effects generated by the
tokenBucket
andloopingCall
modules are terrible. When I try to reason about their behavior, I'm rapidly blocked by a lack of clear semantics forTimer
and other clocks. HowTimer
interacts with vat scheduling is possibly a problem as well.I am relatively sure that my current complaints with
Timer
are driven by the fact that it's currently tricky to put things into their own vats. However, at the same time, knowing how vat scheduling works, I'm not sure how to getTimer
to be sufficiently fair.How does Erlang schedule? Their timers are unsafe but their scheduling is surely worth examining.
Current suggestions for what to build/design:
libuv
uv_hrtime
gives us a high-resolution monotonic timestamp. We can cache it at the beginning of the turn on each vat. Pythontime.time()
gives us a high-resolution wall-clock timestamp, and we can also cache that.