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,32 @@ 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
+ // 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
+
246
282
// Function to track a thread and set its state
247
283
void ThreadPoll (const FunctionCallbackInfo<Value> &args) {
248
284
auto isolate = args.GetIsolate ();
@@ -275,8 +311,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
275
311
if (disable_last_seen) {
276
312
thread_info.last_seen = milliseconds::zero ();
277
313
} 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 ());
280
316
}
281
317
}
282
318
}
@@ -286,8 +322,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
286
322
void GetThreadsLastSeen (const FunctionCallbackInfo<Value> &args) {
287
323
Isolate *isolate = args.GetIsolate ();
288
324
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 ());
291
327
{
292
328
std::lock_guard<std::mutex> lock (threads_mutex);
293
329
for (const auto &[thread_isolate, info] : threads) {
0 commit comments