LotSizingDataModel.Solver 2.0.1
Solver-independent modeling, execution, monitoring and adapter infrastructure.
Loading...
Searching...
No Matches
ExternalSolverProcessRunner.cs
Go to the documentation of this file.
1using System.Diagnostics;
2
4
5/// <summary>
6/// Executes one native solver command-line process with redirected output and
7/// cooperative cancellation.
8/// </summary>
9public sealed class ExternalSolverProcessRunner
10{
11 private readonly object _syncRoot =
12 new();
13
14 private Process? _activeProcess;
15
16 /// <summary>
17 /// Executes a process asynchronously.
18 /// </summary>
19 /// <param name="executablePath">Executable path.</param>
20 /// <param name="arguments">Individual command-line arguments.</param>
21 /// <param name="workingDirectory">Process working directory.</param>
22 /// <param name="standardInput">
23 /// Optional text written to standard input after process start.
24 /// </param>
25 /// <param name="cancellationToken">Cancellation token.</param>
26 /// <returns>Captured process result.</returns>
27 public async Task<ExternalSolverProcessResult> RunAsync(
28 string executablePath,
29 IEnumerable<string> arguments,
30 string workingDirectory,
31 string? standardInput,
32 CancellationToken cancellationToken)
33 {
34 ArgumentException.ThrowIfNullOrWhiteSpace(
35 executablePath);
36 ArgumentNullException.ThrowIfNull(
37 arguments);
38 ArgumentException.ThrowIfNullOrWhiteSpace(
39 workingDirectory);
40
41 var startInfo =
42 new ProcessStartInfo
43 {
44 FileName = executablePath,
45 WorkingDirectory = workingDirectory,
46 UseShellExecute = false,
47 CreateNoWindow = true,
48 RedirectStandardOutput = true,
49 RedirectStandardError = true,
50 RedirectStandardInput = standardInput is not null
51 };
52
53 foreach (string argument in arguments)
54 {
55 startInfo.ArgumentList.Add(
56 argument);
57 }
58
59 using var process =
60 new Process
61 {
62 StartInfo = startInfo,
63 EnableRaisingEvents = true
64 };
65
66 if (!process.Start())
67 {
68 throw new InvalidOperationException(
69 $"Unable to start solver executable '{executablePath}'.");
70 }
71
72 SetActiveProcess(
73 process);
74
75 int cancellationObserved =
76 0;
77
78 using CancellationTokenRegistration registration =
79 cancellationToken.Register(
80 () =>
81 {
82 Interlocked.Exchange(
83 ref cancellationObserved,
84 1);
85
86 Stop();
87 });
88
89 try
90 {
91 Task<string> standardOutputTask =
92 process.StandardOutput.ReadToEndAsync();
93
94 Task<string> standardErrorTask =
95 process.StandardError.ReadToEndAsync();
96
97 if (standardInput is not null)
98 {
99 await process.StandardInput.WriteAsync(
100 standardInput);
101
102 process.StandardInput.Close();
103 }
104
105 await process.WaitForExitAsync(
106 CancellationToken.None);
107
108 string standardOutput =
109 await standardOutputTask;
110
111 string standardError =
112 await standardErrorTask;
113
115 {
116 ExitCode = process.ExitCode,
117 StandardOutput = standardOutput,
118 StandardError = standardError,
119 WasCancelled =
120 Volatile.Read(ref cancellationObserved) != 0 ||
121 cancellationToken.IsCancellationRequested
122 };
123 }
124 finally
125 {
126 ClearActiveProcess(
127 process);
128 }
129 }
130
131 /// <summary>
132 /// Requests termination of the currently active external process.
133 /// </summary>
134 public void Stop()
135 {
136 Process? process;
137
138 lock (_syncRoot)
139 {
140 process =
141 _activeProcess;
142 }
143
144 if (process is null)
145 {
146 return;
147 }
148
149 try
150 {
151 if (!process.HasExited)
152 {
153 process.Kill(
154 entireProcessTree: true);
155 }
156 }
157 catch
158 {
159 // Interruption is best effort. The solve path will report the
160 // process outcome if native termination could not be requested.
161 }
162 }
163
164 private void SetActiveProcess(
165 Process process)
166 {
167 lock (_syncRoot)
168 {
169 _activeProcess =
170 process;
171 }
172 }
173
174 private void ClearActiveProcess(
175 Process process)
176 {
177 lock (_syncRoot)
178 {
179 if (ReferenceEquals(
180 _activeProcess,
181 process))
182 {
183 _activeProcess =
184 null;
185 }
186 }
187 }
188}
Captures the observable result of an external solver process.
Executes one native solver command-line process with redirected output and cooperative cancellation.
async Task< ExternalSolverProcessResult > RunAsync(string executablePath, IEnumerable< string > arguments, string workingDirectory, string? standardInput, CancellationToken cancellationToken)
Executes a process asynchronously.
void Stop()
Requests termination of the currently active external process.