Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 8 additions & 14 deletions Polyfills/Performance/Source/Performance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,6 @@
namespace
{
constexpr const char* JS_INSTANCE_NAME{"performance"};

// Store the start time when the polyfill is initialized
std::chrono::high_resolution_clock::time_point g_startTime;

Napi::Value Now(const Napi::CallbackInfo& info)
{
auto now = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration<double, std::milli>(now - g_startTime);
return Napi::Number::New(info.Env(), duration.count());
}
}

namespace Babylon::Polyfills::Performance
Expand All @@ -24,9 +14,6 @@ namespace Babylon::Polyfills::Performance
{
Napi::HandleScope scope{env};

// Initialize the start time
g_startTime = std::chrono::high_resolution_clock::now();

auto performance = env.Global().Get(JS_INSTANCE_NAME).As<Napi::Object>();
if (!performance.IsUndefined())
{
Expand All @@ -36,6 +23,13 @@ namespace Babylon::Polyfills::Performance
performance = Napi::Object::New(env);
env.Global().Set(JS_INSTANCE_NAME, performance);

performance.Set("now", Napi::Function::New(env, Now, "now"));
// The time origin belongs to this environment, as it does to each browsing context and
// worker: a process-wide origin would be reset by every runtime (worker) that initializes
// the polyfill, moving performance.now() backwards elsewhere and racing with it.
const auto timeOrigin = std::chrono::steady_clock::now();
performance.Set("now", Napi::Function::New(env, [timeOrigin](const Napi::CallbackInfo& info) {
const auto elapsed = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - timeOrigin);
return Napi::Number::New(info.Env(), elapsed.count());
}, "now"));
Comment thread
matthargett marked this conversation as resolved.
}
}
1 change: 1 addition & 0 deletions Tests/UnitTests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(SOURCES
"Source/Tests.DelayedTaskScheduler.cpp"
"Source/Tests.JavaScript.cpp"
"Source/Tests.NodeApi.cpp"
"Source/Tests.Performance.cpp"
"Source/Tests.Scheduling.cpp"
"Source/Tests.StandardStreamLogger.cpp"
"Source/Tests.TimeoutDispatcher.cpp")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ add_library(UnitTestsJNI SHARED
${UNIT_TESTS_DIR}/Source/Tests.NodeApi.cpp
${UNIT_TESTS_DIR}/Source/Tests.Scheduling.cpp
${UNIT_TESTS_DIR}/Source/Tests.StandardStreamLogger.cpp
${UNIT_TESTS_DIR}/Source/Tests.Performance.cpp
${UNIT_TESTS_DIR}/Source/Tests.TimeoutDispatcher.cpp)

if(NAPI_JAVASCRIPT_ENGINE STREQUAL "V8")
Expand Down
43 changes: 43 additions & 0 deletions Tests/UnitTests/Source/Tests.Performance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#include <Babylon/AppRuntime.h>
#include <Babylon/ScriptLoader.h>
#include <Babylon/Polyfills/Performance.h>
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <future>
#include <iostream>
#include <string>

TEST(Performance, TimeOriginIsPerEnvironment)
{
// Regression: performance.now() measured from one process-wide start time that every
// Initialize() rewrote, so bringing up a second runtime moved the first one's clock back to
// zero. Each environment keeps its own origin now, so the first clock stays monotonic.
const auto initialize = [](Babylon::AppRuntime& runtime) {
std::promise<void> done;
runtime.Dispatch([&done](Napi::Env env) {
Babylon::Polyfills::Performance::Initialize(env);
done.set_value();
});
done.get_future().get();
};
const auto now = [](Babylon::AppRuntime& runtime) {
std::promise<double> value;
runtime.Dispatch([&value](Napi::Env env) {
auto performance = env.Global().Get("performance").As<Napi::Object>();
value.set_value(performance.Get("now").As<Napi::Function>().Call(performance, {}).As<Napi::Number>().DoubleValue());
});
return value.get_future().get();
};

Babylon::AppRuntime first{};
initialize(first);
std::this_thread::sleep_for(std::chrono::milliseconds{50});
const double before = now(first);

Babylon::AppRuntime second{};
initialize(second);
const double after = now(first);

EXPECT_GE(after, before);
}