3c0e5ea4d7
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
38 lines
1.7 KiB
C#
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();
|
|
}
|
|
}
|