Skip to content

Commit 433b304

Browse files
when tracking threads, use a monotonic clock that ignores system suspension
Using the system time causes each suspension to be detected as an event loop delay which is not useful. System suspensions may be common when running in low cost cloud environments where systems are suspended when not actively serving requests.
1 parent 56f301a commit 433b304

File tree

1 file changed

+39
-4
lines changed

1 file changed

+39
-4
lines changed

module.cc

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
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+
616
using namespace v8;
717
using namespace node;
818
using namespace std::chrono;
@@ -243,6 +253,31 @@ 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+
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+
246281
// Function to track a thread and set its state
247282
void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
248283
auto isolate = args.GetIsolate();
@@ -275,8 +310,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
275310
if (disable_last_seen) {
276311
thread_info.last_seen = milliseconds::zero();
277312
} 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());
280315
}
281316
}
282317
}
@@ -286,8 +321,8 @@ void ThreadPoll(const FunctionCallbackInfo<Value> &args) {
286321
void GetThreadsLastSeen(const FunctionCallbackInfo<Value> &args) {
287322
Isolate *isolate = args.GetIsolate();
288323
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());
291326
{
292327
std::lock_guard<std::mutex> lock(threads_mutex);
293328
for (const auto &[thread_isolate, info] : threads) {

0 commit comments

Comments
 (0)