From cb0a288d70a985ff170841e651cca9fa8bd0ff94 Mon Sep 17 00:00:00 2001 From: Ilona Tomkowicz Date: Wed, 25 Feb 2026 09:35:36 +0100 Subject: [PATCH] Fix. --- .../ConsoleArguments/CommandLineOptions.cs | 3 +++ .../ConsoleArguments/ConfigParser.cs | 3 ++- .../Environments/Runtimes/WasmRuntime.cs | 10 +++++++++- .../Templates/benchmark-main.mjs | 20 ++++++++++++++++++- .../Toolchains/MonoWasm/WasmExecutor.cs | 6 ++++-- 5 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs index 44ea9447de..06971a2981 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/CommandLineOptions.cs @@ -222,6 +222,9 @@ public bool UseDisassemblyDiagnoser [Option("wasmCoreCLR", Required = false, Default = false, HelpText = "Use CoreCLR runtime pack (Microsoft.NETCore.App.Runtime.browser-wasm) instead of the Mono runtime pack for WASM benchmarks.")] public bool WasmCoreCLR { get; set; } + [Option("wasmProcessTimeout", Required = false, Default = 10, HelpText = "Maximum time in minutes to wait for a single WASM benchmark process to finish before force killing it.")] + public int WasmProcessTimeoutMinutes { get; set; } + [Option("noForcedGCs", Required = false, HelpText = "Specifying would not forcefully induce any GCs.")] public bool NoForcedGCs { get; set; } diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index 306462974b..b63e495a50 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -706,7 +706,8 @@ private static Job MakeWasmJob(Job baseJob, CommandLineOptions options, string m aot: wasmAot, wasmDataDir: options.WasmDataDirectory?.FullName ?? "", moniker: moniker, - isMonoRuntime: !options.WasmCoreCLR); + isMonoRuntime: !options.WasmCoreCLR, + processTimeoutMinutes: options.WasmProcessTimeoutMinutes); var toolChain = WasmToolchain.From(new NetCoreAppSettings( targetFrameworkMoniker: wasmRuntime.MsBuildMoniker, diff --git a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs index 0c148c0352..0ac924c9d4 100644 --- a/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs +++ b/src/BenchmarkDotNet/Environments/Runtimes/WasmRuntime.cs @@ -26,6 +26,11 @@ public class WasmRuntime : Runtime, IEquatable /// public bool IsMonoRuntime { get; } + /// + /// Maximum time in minutes to wait for a single benchmark process to finish before force killing it. Default is 10 minutes. + /// + public int ProcessTimeoutMinutes { get; } + /// /// creates new instance of WasmRuntime /// @@ -37,6 +42,7 @@ public class WasmRuntime : Runtime, IEquatable /// Specifies a wasm data directory surfaced as $(WasmDataDir) for the project /// Runtime moniker /// When true (default), use Mono runtime pack; when false, use CoreCLR runtime pack. + /// Maximum time in minutes to wait for a single benchmark process to finish. Default is 10. public WasmRuntime( string msBuildMoniker = "net8.0", string displayName = "Wasm", @@ -45,7 +51,8 @@ public WasmRuntime( bool aot = false, string wasmDataDir = "", RuntimeMoniker moniker = RuntimeMoniker.WasmNet80, - bool isMonoRuntime = true) + bool isMonoRuntime = true, + int processTimeoutMinutes = 10) : base(moniker, msBuildMoniker, displayName) { if (javaScriptEngine.IsNotBlank() && javaScriptEngine != "v8" && !File.Exists(javaScriptEngine)) @@ -56,6 +63,7 @@ public WasmRuntime( Aot = aot; WasmDataDir = wasmDataDir; IsMonoRuntime = isMonoRuntime; + ProcessTimeoutMinutes = processTimeoutMinutes; } public override bool Equals(object? obj) diff --git a/src/BenchmarkDotNet/Templates/benchmark-main.mjs b/src/BenchmarkDotNet/Templates/benchmark-main.mjs index c9f705d547..7acbbb2a23 100644 --- a/src/BenchmarkDotNet/Templates/benchmark-main.mjs +++ b/src/BenchmarkDotNet/Templates/benchmark-main.mjs @@ -1,9 +1,27 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +const ENVIRONMENT_IS_NODE = typeof process == "object" && typeof process.versions == "object" && typeof process.versions.node == "string"; +const ENVIRONMENT_IS_WEB_WORKER = typeof importScripts == "function"; +const ENVIRONMENT_IS_WEB = typeof window == "object" || (ENVIRONMENT_IS_WEB_WORKER && !ENVIRONMENT_IS_NODE); + +if (!ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WEB && typeof globalThis.crypto === 'undefined') { + // **NOTE** this is a simple insecure polyfill for JS shells (like v8/d8) that lack Web Crypto API. + // /dev/random doesn't work on js shells, so define our own. + globalThis.crypto = { + getRandomValues: function (buffer) { + for (let i = 0; i < buffer.length; i++) + buffer[i] = (Math.random() * 256) | 0; + } + } +} + import { dotnet } from './_framework/dotnet.js' +// Get command line arguments: Node.js uses process.argv, v8 uses arguments/scriptArgs +const args = typeof process !== 'undefined' ? process.argv.slice(2) : (typeof arguments !== 'undefined' ? [...arguments] : (typeof scriptArgs !== 'undefined' ? scriptArgs : [])); + await dotnet .withDiagnosticTracing(false) - .withApplicationArguments(...arguments) + .withApplicationArguments(...args) .run() diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs index 4534670f50..01d73f2ba4 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmExecutor.cs @@ -89,9 +89,11 @@ private static ExecuteResult Execute(Process process, BenchmarkCase benchmarkCas process.TrySetAffinity(benchmarkCase.Job.Environment.Affinity, logger); } - if (!process.WaitForExit(milliseconds: (int)TimeSpan.FromMinutes(10).TotalMilliseconds)) + WasmRuntime wasmRuntime = (WasmRuntime)benchmarkCase.GetRuntime(); + int timeoutMinutes = wasmRuntime.ProcessTimeoutMinutes; + if (!process.WaitForExit(milliseconds: (int)TimeSpan.FromMinutes(timeoutMinutes).TotalMilliseconds)) { - logger.WriteLineInfo("// The benchmarking process did not finish within 10 minutes, it's going to get force killed now."); + logger.WriteLineInfo($"// The benchmarking process did not finish within {timeoutMinutes} minutes, it's going to get force killed now."); processOutputReader.CancelRead(); consoleExitHandler.KillProcessTree();