33#include < mutex>
44#include < node.h>
55
6+ // Platform-specific includes for time functions
7+ #ifdef _WIN32
8+ #include < realtimeapiset.h>
9+ #include < windows.h>
10+ #elif defined(__APPLE__)
11+ #include < time.h>
12+ #elif defined(__linux__)
13+ #include < time.h>
14+ #endif
15+
616using namespace v8 ;
717using namespace node ;
818using namespace std ::chrono;
@@ -243,6 +253,32 @@ void RegisterThread(const FunctionCallbackInfo<Value> &args) {
243253 }
244254}
245255
256+ // Cross-platform monotonic time function. Provides a monotonic clock that only
257+ // increases and does not tick when the system is suspended.
258+ steady_clock::time_point GetUnbiasedMonotonicTime () {
259+ #ifdef _WIN32
260+ // Windows: QueryUnbiasedInterruptTimePrecise returns time in 100-nanosecond
261+ // units
262+ ULONGLONG interrupt_time;
263+ QueryUnbiasedInterruptTimePrecise (&interrupt_time);
264+ // Convert from 100-nanosecond units to nanoseconds
265+ uint64_t time_ns = interrupt_time * 100 ;
266+ return steady_clock::time_point (nanoseconds (time_ns));
267+ #elif defined(__APPLE__)
268+ // macOS: CLOCK_UPTIME_RAW returns time in nanoseconds
269+ uint64_t time_ns = clock_gettime_nsec_np (CLOCK_UPTIME_RAW);
270+ return steady_clock::time_point (nanoseconds (time_ns));
271+ #elif defined(__linux__)
272+ struct timespec ts;
273+ clock_gettime (CLOCK_MONOTONIC, &ts);
274+ return steady_clock::time_point (seconds (ts.tv_sec ) + nanoseconds (ts.tv_nsec ));
275+ #else
276+ // Fallback for other platforms using steady_clock. Note: this will be
277+ // monotonic but is not gaurenteed to ignore time spent while suspended.
278+ return steady_clock::now ();
279+ #endif
280+ }
281+
246282// Function to track a thread and set its state
247283void ThreadPoll (const FunctionCallbackInfo<Value> &args) {
248284 auto isolate = args.GetIsolate ();
@@ -275,8 +311,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
275311 if (disable_last_seen) {
276312 thread_info.last_seen = milliseconds::zero ();
277313 } else {
278- thread_info.last_seen =
279- duration_cast<milliseconds>( system_clock::now ().time_since_epoch ());
314+ thread_info.last_seen = duration_cast<milliseconds>(
315+ GetUnbiasedMonotonicTime ().time_since_epoch ());
280316 }
281317 }
282318 }
@@ -286,8 +322,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
286322void GetThreadsLastSeen (const FunctionCallbackInfo<Value> &args) {
287323 Isolate *isolate = args.GetIsolate ();
288324 Local<Object> result = Object::New (isolate);
289- milliseconds now =
290- duration_cast<milliseconds>( system_clock::now ().time_since_epoch ());
325+ milliseconds now = duration_cast<milliseconds>(
326+ GetUnbiasedMonotonicTime ().time_since_epoch ());
291327 {
292328 std::lock_guard<std::mutex> lock (threads_mutex);
293329 for (const auto &[thread_isolate, info] : threads) {
0 commit comments