Repository: denoland/deno
Pull request: fix(ext/node): implement process.threadCpuUsage()

- Implement `process.threadCpuUsage()` returning per-thread CPU usage (user/system in microseconds)
- Add `op_current_thread_cpu_usage` Rust op reusing existing platform-specific thread CPU usage infrastructure (mach `thread_info` on macOS, `/proc/self/task` on Linux, `GetThreadTimes` on Windows)
- Supports optional `prevValue` argument to return diff, matching Node.js API

Changed files:
filename: ext/node/polyfills/process.ts
patch: @@ -8,6 +8,7 @@ import { core, internals, primordials } from "ext:core/mod.js";
 import { initializeDebugEnv } from "ext:deno_node/internal/util/debuglog.ts";
 import { format } from "ext:deno_node/internal/util/inspect.mjs";
 import {
+  op_current_thread_cpu_usage,
   op_fs_umask,
   op_getegid,
   op_geteuid,
@@ -228,6 +229,46 @@ export function cpuUsage(previousValue?: CpuUsage): CpuUsage {
   return cpuValues;
 }
 
+const threadCpuValues = new Float64Array(2);
+
+export function threadCpuUsage(
+  previousValue?: CpuUsage,
+): CpuUsage {
+  if (previousValue) {
+    if (!previousCpuUsageValueIsValid(previousValue.user)) {
+      validateObject(previousValue, "prevValue");
+
+      validateNumber(previousValue.user, "prevValue.user");
+      throw new ERR_INVALID_ARG_VALUE_RANGE(
+        "prevValue.user",
+        previousValue.user,
+      );
+    }
+
+    if (!previousCpuUsageValueIsValid(previousValue.system)) {
+      validateNumber(previousValue.system, "prevValue.system");
+      throw new ERR_INVALID_ARG_VALUE_RANGE(
+        "prevValue.system",
+        previousValue.system,
+      );
+    }
+  }
+
+  op_current_thread_cpu_usage(threadCpuValues);
+
+  if (previousValue) {
+    return {
+      user: threadCpuValues[0] - previousValue.user,
+      system: threadCpuValues[1] - previousValue.system,
+    };
+  }
+
+  return {
+    user: threadCpuValues[0],
+    system: threadCpuValues[1],
+  };
+}
+
 function createWarningObject(
   warning: string,
   type: string,
@@ -751,6 +792,7 @@ Object.defineProperty(process, "config", {
 });
 
 process.cpuUsage = cpuUsage;
+process.threadCpuUsage = threadCpuUsage;
 
 /** https://nodejs.org/api/process.html#process_process_cwd */
 process.cwd = cwd;

filename: runtime/ops/worker_host.rs
patch: @@ -107,6 +107,7 @@ deno_core::extension!(
     op_host_recv_ctrl,
     op_host_recv_message,
     op_host_get_worker_cpu_usage,
+    op_current_thread_cpu_usage,
   ],
   options = {
     create_web_worker_cb: Arc<CreateWebWorkerCb>,
@@ -477,6 +478,14 @@ fn op_host_get_worker_cpu_usage(
   out[1] = 0.0;
 }
 
+#[op2(fast)]
+fn op_current_thread_cpu_usage(#[buffer] out: &mut [f64]) {
+  let handle = capture_current_thread_handle();
+  let (user, system) = get_thread_cpu_usage_by_handle(handle);
+  out[0] = user;
+  out[1] = system;
+}
+
 #[cfg(target_os = "macos")]
 fn capture_current_thread_handle() -> u64 {
   // SAFETY: FFI call to get the current thread's Mach port.

filename: tests/node_compat/config.jsonc
patch: @@ -1697,6 +1697,8 @@
     "parallel/test-process-really-exit.js": {},
     "parallel/test-process-title-cli.js": {},
     "parallel/test-process-release.js": {},
+    "parallel/test-process-threadCpuUsage-main-thread.js": {},
+    "parallel/test-process-threadCpuUsage-worker-threads.js": {},
     "parallel/test-process-umask-mask.js": {},
     "parallel/test-process-umask.js": {},
     "parallel/test-process-uptime.js": {},