3
3
#include < mutex>
4
4
#include < node.h>
5
5
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
+
6
16
using namespace v8 ;
7
17
using namespace node ;
8
18
using namespace std ::chrono;
@@ -243,6 +253,31 @@ void RegisterThread(const FunctionCallbackInfo<Value> &args) {
243
253
}
244
254
}
245
255
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
+ uint64_t time_ns = clock_gettime_nsec_np (CLOCK_UPTIME_RAW);
269
+ return steady_clock::time_point (nanoseconds (time_ns));
270
+ #elif defined(__linux__)
271
+ struct timespec ts;
272
+ clock_gettime (CLOCK_MONOTONIC, &ts);
273
+ return steady_clock::time_point (seconds (ts.tv_sec ) + nanoseconds (ts.tv_nsec ));
274
+ #else
275
+ // Fallback for other platforms using steady_clock. Note: this will be
276
+ // monotonic but is not gaurenteed to ignore time spent while suspended.
277
+ return steady_clock::now ();
278
+ #endif
279
+ }
280
+
246
281
// Function to track a thread and set its state
247
282
void ThreadPoll (const FunctionCallbackInfo<Value> &args) {
248
283
auto isolate = args.GetIsolate ();
@@ -275,8 +310,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
275
310
if (disable_last_seen) {
276
311
thread_info.last_seen = milliseconds::zero ();
277
312
} else {
278
- thread_info.last_seen =
279
- duration_cast<milliseconds>( system_clock::now ().time_since_epoch ());
313
+ thread_info.last_seen = duration_cast<milliseconds>(
314
+ GetUnbiasedMonotonicTime ().time_since_epoch ());
280
315
}
281
316
}
282
317
}
@@ -286,8 +321,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
286
321
void GetThreadsLastSeen (const FunctionCallbackInfo<Value> &args) {
287
322
Isolate *isolate = args.GetIsolate ();
288
323
Local<Object> result = Object::New (isolate);
289
- milliseconds now =
290
- duration_cast<milliseconds>( system_clock::now ().time_since_epoch ());
324
+ milliseconds now = duration_cast<milliseconds>(
325
+ GetUnbiasedMonotonicTime ().time_since_epoch ());
291
326
{
292
327
std::lock_guard<std::mutex> lock (threads_mutex);
293
328
for (const auto &[thread_isolate, info] : threads) {
0 commit comments