]> git.scottworley.com Git - vopamoi/commitdiff
Use a monotonic clock
authorScott Worley <scottworley@scottworley.com>
Thu, 27 Jan 2022 06:33:53 +0000 (22:33 -0800)
committerScott Worley <scottworley@scottworley.com>
Thu, 27 Jan 2022 20:21:55 +0000 (12:21 -0800)
Generally assume that clock values always increase to simplify reasoning
everywhere clock values are used.  Then enforce that assumption.

Clock values don't really, truly always go up because page reloads &
multiple devices, but since I'm not currently planning to spend the
effort that would be required to perfectly handle this in all cases,
making it happen in fewer cases is a win (rather than just making bugs
I want to fix harder to reproduce).

vopamoi.ts

index 04c5d414be7a46190e4d5679991b0d1edc4cd38e..271354fc7aa3a52192f092a88a9df8de2f1fb341 100644 (file)
@@ -13,6 +13,22 @@ function splitN(str: string, delimiter: string, limit: number = MAX_SAFE_INTEGER
   return at === -1 ? [str] : [str.substring(0, at)].concat(splitN(str.substring(at + delimiter.length), delimiter, limit - 1));
 }
 
+// A clock that never goes backwards; monotonic.
+function Clock() {
+  var previousNow = Date.now();
+  return {
+    now: function (): number {
+      const now = Date.now();
+      if (now > previousNow) {
+        previousNow = now;
+        return now;
+      }
+      return ++previousNow;
+    },
+  };
+}
+const clock = Clock();
+
 const Model = {
   addTask: function (timestamp: string, description: string): Element {
     const task = document.createElement("div");
@@ -143,25 +159,25 @@ const undoLog: string[] = [];
 
 const UI = {
   addTask: function (description: string): Element {
-    const now = Date.now();
+    const now = clock.now();
     undoLog.push(`State ${now} deleted`);
     return <Element>log.recordAndApply(`${now} Create ${description}`);
   },
   edit: function (createTimestamp: string, newDescription: string, oldDescription: string) {
     undoLog.push(`Edit ${createTimestamp} ${oldDescription}`);
-    return log.recordAndApply(`${Date.now()} Edit ${createTimestamp} ${newDescription}`);
+    return log.recordAndApply(`${clock.now()} Edit ${createTimestamp} ${newDescription}`);
   },
   setPriority: function (createTimestamp: string, newPriority: number, oldPriority: number) {
     undoLog.push(`Priority ${createTimestamp} ${oldPriority}`);
-    return log.recordAndApply(`${Date.now()} Priority ${createTimestamp} ${newPriority}`);
+    return log.recordAndApply(`${clock.now()} Priority ${createTimestamp} ${newPriority}`);
   },
   setState: function (createTimestamp: string, newState: string, oldState: string) {
     undoLog.push(`State ${createTimestamp} ${oldState}`);
-    return log.recordAndApply(`${Date.now()} State ${createTimestamp} ${newState}`);
+    return log.recordAndApply(`${clock.now()} State ${createTimestamp} ${newState}`);
   },
   undo: function () {
     if (undoLog.length > 0) {
-      return log.recordAndApply(`${Date.now()} ${undoLog.pop()}`);
+      return log.recordAndApply(`${clock.now()} ${undoLog.pop()}`);
     }
   },
 };
@@ -267,7 +283,7 @@ const BrowserUI = {
   // Change task's priority to be between other tasks a and b.
   setPriority: function (task: Element, a: Element | null, b: Element | null) {
     const aPriority = a === null ? 0 : Model.getPriority(a);
-    const bPriority = b === null ? Date.now() : Model.getPriority(b);
+    const bPriority = b === null ? clock.now() : Model.getPriority(b);
     console.assert(aPriority < bPriority, aPriority, "<", bPriority);
     const span = bPriority - aPriority;
     const newPriority = aPriority + 0.1 * span + 0.8 * span * Math.random();