Files
lmxopcua/src/Server/ZB.MOM.WW.OtOpcUa.ScriptWorker/Program.cs
T
Joseph Doherty 3c0e5ea4d7 perf(scripting): #789 Windows worker pipe I/O blocks on dedicated threads, off the completion port
A .NET pipe opened Asynchronous completes even its synchronous Read/Write
through the thread pool's completion port, so every frame on Windows cost a
completion packet plus a pool work item (and the pool's spin-wait). New
WindowsBlockingPipeStream issues its own overlapped ReadFile/WriteFile with the
OVERLAPPED.hEvent low bit set (no completion packet) and waits on that event on
the calling thread; reads and writes stay concurrent, unlike a synchronous
handle. ScriptWorkerFrameCodec.BlockingStream picks it on Windows and the pipe
itself on Unix, so both ends now use the dedicated blocking reader + synchronous
combining writer on every OS (UseBlockingIo and the async read loops are gone).

Claude-Session: https://claude.ai/code/session_01GT67AD99zQQqpLCVm3p7xk
2026-11-11 19:11:10 -05:00

38 lines
1.7 KiB
C#

using System.IO.Pipes;
using ZB.MOM.WW.OtOpcUa.Core.Scripting.Protocol;
namespace ZB.MOM.WW.OtOpcUa.ScriptWorker;
/// <summary>Entry point: connect to the host's pipe named in the environment and serve until told to stop.</summary>
internal static class Program
{
private static async Task<int> Main()
{
var pipeName = Environment.GetEnvironmentVariable(ScriptWorkerEnvironment.PipeName);
var nonce = Environment.GetEnvironmentVariable(ScriptWorkerEnvironment.Nonce);
if (string.IsNullOrEmpty(pipeName) || string.IsNullOrEmpty(nonce))
{
await Console.Error.WriteLineAsync("ScriptWorker: launched without a pipe name or nonce; exiting.");
return ScriptWorkerEnvironment.ExitProtocolFault;
}
_ = int.TryParse(Environment.GetEnvironmentVariable(ScriptWorkerEnvironment.ParentPid), out var parentPid);
// CurrentUserOnly: the client verifies the server end is owned by this account as well.
var pipe = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut,
PipeOptions.Asynchronous | PipeOptions.CurrentUserOnly);
try
{
// Synchronous, so the socket under a Unix pipe stays blocking (ScriptWorkerFrameCodec.BlockingStream).
pipe.Connect(TimeSpan.FromSeconds(10));
}
catch (Exception ex)
{
await Console.Error.WriteLineAsync($"ScriptWorker: could not connect to the host pipe: {ex.Message}");
return ScriptWorkerEnvironment.ExitProtocolFault;
}
var host = new WorkerHost(ScriptWorkerFrameCodec.BlockingStream(pipe), nonce, parentPid);
return await host.RunAsync();
}
}