Bug 1457560 - Expose $262.agent.monotonicNow() for Test262 use in testing Atomic operation timeouts. r=jwalden
authorRick Waldron <waldron.rick@gmail.com>
Wed, 06 Jun 2018 22:21:34 -0700
changeset 476137 51238c5a88c51fc4e35bf4571c9c4e6a3dc79821
parent 476136 11c5f8019bca30e59f0a99df346b796d6d6f1aa6
child 476138 75662fe4c9c525111f8a60d5de273aef22b9a65c
push id9374
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:43:20 +0000
treeherdermozilla-beta@160e085dfb0b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwalden
bugs1457560
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1457560 - Expose $262.agent.monotonicNow() for Test262 use in testing Atomic operation timeouts. r=jwalden
js/src/builtin/TestingFunctions.cpp
js/src/tests/test262-host.js
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -8,20 +8,27 @@
 
 #include "mozilla/Atomics.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Move.h"
 #include "mozilla/Sprintf.h"
 #include "mozilla/Unused.h"
 
+#include <cfloat>
 #include <cmath>
 #include <cstdlib>
 #include <ctime>
 
+#if defined(XP_UNIX) && !defined(XP_DARWIN)
+#include <time.h>
+#else
+#include <chrono>
+#endif
+
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "builtin/Promise.h"
 #include "builtin/SelfHostingDefines.h"
 #ifdef DEBUG
 #include "frontend/TokenStream.h"
 #include "irregexp/RegExpAST.h"
@@ -4994,16 +5001,66 @@ AflLoop(JSContext* cx, unsigned argc, Va
         return false;
 
     args.rval().setBoolean(!!__AFL_LOOP(max_cnt));
     return true;
 }
 #endif
 
 static bool
+MonotonicNow(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    double now;
+
+// The std::chrono symbols are too new to be present in STL on all platforms we
+// care about, so use raw POSIX clock APIs when it might be necessary.
+#if defined(XP_UNIX) && !defined(XP_DARWIN)
+    auto ComputeNow = [](const timespec& ts) {
+        return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
+    };
+
+    timespec ts;
+    if (false && clock_gettime(CLOCK_MONOTONIC, &ts) == 0) {
+        // Use a monotonic clock if available.
+        now = ComputeNow(ts);
+    } else {
+        // Use a realtime clock as fallback.
+        if (clock_gettime(CLOCK_REALTIME, &ts) != 0) {
+            // Fail if no clock is available.
+            JS_ReportErrorASCII(cx, "can't retrieve system clock");
+            return false;
+        }
+
+        now = ComputeNow(ts);
+
+        // Manually enforce atomicity on a non-monotonic clock.
+        {
+            static mozilla::Atomic<bool, mozilla::ReleaseAcquire> spinLock;
+            while (!spinLock.compareExchange(false, true))
+                continue;
+
+            static double lastNow = -FLT_MAX;
+            now = lastNow = std::max(now, lastNow);
+
+            spinLock = false;
+        }
+    }
+#else
+    using std::chrono::duration_cast;
+    using std::chrono::milliseconds;
+    using std::chrono::steady_clock;
+    now = duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count();
+#endif // XP_UNIX && !XP_DARWIN
+
+    args.rval().setNumber(now);
+    return true;
+}
+
+static bool
 TimeSinceCreation(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     double when = (mozilla::TimeStamp::Now() -
                    mozilla::TimeStamp::ProcessCreation()).ToMilliseconds();
     args.rval().setNumber(when);
     return true;
 }
@@ -5890,16 +5947,21 @@ gc::ZealModeHelpText),
 "  Get the value of a bound name in a module environment.\n"),
 
 #if defined(FUZZING) && defined(__AFL_COMPILER)
     JS_FN_HELP("aflloop", AflLoop, 1, 0,
 "aflloop(max_cnt)",
 "  Call the __AFL_LOOP() runtime function (see AFL docs)\n"),
 #endif
 
+    JS_FN_HELP("monotonicNow", MonotonicNow, 0, 0,
+"monotonicNow()",
+"  Return a timestamp reflecting the current elapsed system time.\n"
+"  This is monotonically increasing.\n"),
+
     JS_FN_HELP("timeSinceCreation", TimeSinceCreation, 0, 0,
 "TimeSinceCreation()",
 "  Returns the time in milliseconds since process creation.\n"
 "  This uses a clock compatible with the profiler.\n"),
 
     JS_FN_HELP("isConstructor", IsConstructor, 1, 0,
 "isConstructor(value)",
 "  Returns whether the value is considered IsConstructor.\n"),
--- a/js/src/tests/test262-host.js
+++ b/js/src/tests/test262-host.js
@@ -8,16 +8,17 @@
     var ReflectApply = global.Reflect.apply;
     var NewGlobal = global.newGlobal;
     var Atomics = global.Atomics;
     var SharedArrayBuffer = global.SharedArrayBuffer;
     var Int32Array = global.Int32Array;
     var setSharedArrayBuffer = global.setSharedArrayBuffer;
     var getSharedArrayBuffer = global.getSharedArrayBuffer;
     var evalInWorker = global.evalInWorker;
+    var monotonicNow = global.monotonicNow;
 
     var hasCreateIsHTMLDDA = "createIsHTMLDDA" in global;
     var hasThreads = ("helperThreadCount" in global ? global.helperThreadCount() > 0 : true);
     var hasMailbox = typeof setSharedArrayBuffer == "function" && typeof getSharedArrayBuffer == "function";
     var hasEvalInWorker = typeof evalInWorker == "function";
 
     if (!hasCreateIsHTMLDDA && !("document" in global && "all" in global.document))
         throw new Error("no [[IsHTMLDDA]] object available for testing");
@@ -123,17 +124,19 @@
             Atomics.add(_ia, ${_NUMTXT_LOC}, 1);
             Atomics.store(_ia, ${_LOCKTXT_LOC}, 0);
         },
 
         sleep(s) {
             Atomics.wait(_ia, ${_SLEEP_LOC}, 0, s);
         },
 
-        leaving() {}
+        leaving() {},
+
+        monotonicNow,
     };
     Atomics.add(_ia, ${_RDY_LOC}, 1);
     return agent;
 })();`;
 // END WORKER PREFIX
 
             return {
                 _numWorkers: 0,
@@ -185,16 +188,18 @@
                     this._numReports++;
                     return s;
                 },
 
                 sleep(s) {
                     this._bailIfNotAvailable();
                     Atomics.wait(_ia, _SLEEP_LOC, 0, s);
                 },
+
+                monotonicNow,
             };
         })()
     };
 })(this);
 
 var $mozAsyncTestDone = false;
 function $DONE(failure) {
     // This function is generally called from within a Promise handler, so any