@@ -12,9 +12,15 @@ public sealed class Command
1212 private Process _process ;
1313 private bool _isRunning = false ;
1414 private CommandDefinition _definition ;
15+ private CancellationToken _token ;
1516
1617 private Channel < ConsoleMessage > ? _buffer ;
1718
19+ /// <summary>
20+ /// Exit Code of the process. Will be <see langword="null"/> if the process is still running.
21+ /// </summary>
22+ public int ? ExitCode => _isRunning ? null : _process . ExitCode ;
23+
1824 internal Command ( CommandDefinition definition )
1925 {
2026 _definition = definition ;
@@ -24,76 +30,76 @@ internal Command(CommandDefinition definition)
2430 ConfigureProcess ( ) ;
2531 }
2632
27- private void ConfigureProcess ( )
33+ /// <summary>
34+ /// Attempts to run the configured process.
35+ /// </summary>
36+ /// <returns><see langword="true"/> if the process starts successfully. <see langword="false"/> if the process failed or was already running</returns>
37+ public bool Start ( CancellationToken cancellationToken )
2838 {
29- _process . EnableRaisingEvents = true ;
39+ if ( _isRunning ) return false ;
3040
31- _process . Exited += ( sender , args ) =>
32- {
33- _isRunning = false ;
41+ _isRunning = true ;
3442
35- if ( _buffer == null )
36- return ;
43+ CreateOutputBuffer ( ) ;
3744
38- _buffer ? . Writer . TryComplete ( ) ;
39- } ;
45+ _isRunning = _process . Start ( ) ;
4046
41- if ( _definition . RelayOutput )
47+ if ( _isRunning )
4248 {
43- _process . OutputDataReceived += ( sender , args ) =>
44- {
45- if ( ! string . IsNullOrWhiteSpace ( args . Data ) )
46- _buffer ! . Writer . TryWrite ( new ConsoleMessage ( this , args . Data , MessageType . Output ) ) ;
47- } ;
49+ var s = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
50+ _token = s . Token ;
51+ _token . Register ( Kill ) ;
52+ }
4853
49- _process . ErrorDataReceived += ( sender , args ) =>
50- {
51- if ( ! string . IsNullOrWhiteSpace ( args . Data ) )
52- _buffer ! . Writer . TryWrite ( new ConsoleMessage ( this , args . Data , MessageType . Error ) ) ;
53- } ;
54+ if ( _definition . RelayOutput )
55+ {
56+ _process . BeginOutputReadLine ( ) ;
57+ _process . BeginErrorReadLine ( ) ;
5458 }
55- }
5659
57- private void CreateOutputBuffer ( )
58- {
59- _buffer = _definition . MaxBufferSize . HasValue
60- ? Channel . CreateBounded < ConsoleMessage > (
61- new BoundedChannelOptions ( _definition . MaxBufferSize . Value )
62- {
63- SingleReader = true ,
64- FullMode = BoundedChannelFullMode . DropOldest
65- } )
66- : Channel . CreateUnbounded < ConsoleMessage > (
67- new UnboundedChannelOptions ( )
68- {
69- SingleReader = true ,
70- }
71- ) ;
60+ return _isRunning ;
7261 }
7362
7463 /// <summary>
75- /// Attempts to run the configured process.
64+ /// Attempts to run the configured process asynchronously .
7665 /// </summary>
77- /// <returns><see langword="true"/> if the process starts successfully. <see langword="false"/> if the process failed or was already running</returns>
78- public bool Start ( )
66+ /// <param name="cancellationToken">Token used to cancel the process</param>
67+ /// <returns></returns>
68+ public Task < bool > StartAsync ( CancellationToken cancellationToken )
7969 {
80- if ( _isRunning ) return false ;
70+ if ( _isRunning )
71+ return Task . FromResult ( false ) ;
8172
8273 _isRunning = true ;
8374
8475 CreateOutputBuffer ( ) ;
8576
86- _isRunning = _process . Start ( ) ;
87-
88- if ( _definition . RelayOutput )
77+ return Task . Run ( ( ) =>
8978 {
90- _process . BeginOutputReadLine ( ) ;
91- _process . BeginErrorReadLine ( ) ;
92- }
79+ _isRunning = _process . Start ( ) ;
9380
94- return _isRunning ;
81+ if ( _isRunning )
82+ {
83+ var s = CancellationTokenSource . CreateLinkedTokenSource ( cancellationToken ) ;
84+ _token = s . Token ;
85+ _token . Register ( Kill ) ;
86+ }
87+
88+ if ( _definition . RelayOutput )
89+ {
90+ _process . BeginOutputReadLine ( ) ;
91+ _process . BeginErrorReadLine ( ) ;
92+ }
93+
94+ return _isRunning ;
95+ } ) ;
9596 }
9697
98+ /// <summary>
99+ /// Kills the process if it is running.
100+ /// </summary>
101+ public void Kill ( ) => _process . Kill ( ) ;
102+
97103 /// <summary>
98104 /// If "M:ReProcess.CommandBuilder.WithOutput" was called, this method will return the output of the process as it is written to StdOut and StdErr.
99105 /// </summary>
@@ -106,18 +112,16 @@ public async IAsyncEnumerable<ConsoleMessage> ReadOutputAsync([EnumeratorCancell
106112
107113 await foreach ( var consoleMessage in _buffer . Reader . ReadAllAsync ( cancellationToken ) )
108114 {
115+ if ( _token . IsCancellationRequested )
116+ yield break ; // Exit early if cancellation was requested from the start token
117+
109118 yield return consoleMessage ;
110119
111- if ( ! _definition . UseAggressiveOutputProcessing )
120+ if ( ! _definition . UseAggressiveOutputProcessing )
112121 await Task . Delay ( 10 ) ;
113122 }
114123 }
115124
116- /// <summary>
117- /// Kills the process if it is running.
118- /// </summary>
119- public void Kill ( ) => _process . Kill ( ) ;
120-
121125 /// <summary>
122126 /// Waits for the process to exit.
123127 /// </summary>
@@ -132,5 +136,52 @@ public async Task<int> WaitForExitAsync()
132136
133137 return _process . ExitCode ;
134138 }
139+
140+ private void ConfigureProcess ( )
141+ {
142+ _process . EnableRaisingEvents = true ;
143+
144+ _process . Exited += ( sender , args ) =>
145+ {
146+ _isRunning = false ;
147+
148+ if ( _buffer == null )
149+ return ;
150+
151+ _buffer ? . Writer . TryComplete ( ) ;
152+ } ;
153+
154+ if ( _definition . RelayOutput )
155+ {
156+ _process . OutputDataReceived += ( sender , args ) =>
157+ {
158+ if ( ! string . IsNullOrWhiteSpace ( args . Data ) )
159+ _buffer ! . Writer . TryWrite ( new ConsoleMessage ( this , args . Data , MessageType . Output ) ) ;
160+ } ;
161+
162+ _process . ErrorDataReceived += ( sender , args ) =>
163+ {
164+ if ( ! string . IsNullOrWhiteSpace ( args . Data ) )
165+ _buffer ! . Writer . TryWrite ( new ConsoleMessage ( this , args . Data , MessageType . Error ) ) ;
166+ } ;
167+ }
168+ }
169+
170+ private void CreateOutputBuffer ( )
171+ {
172+ _buffer = _definition . MaxBufferSize . HasValue
173+ ? Channel . CreateBounded < ConsoleMessage > (
174+ new BoundedChannelOptions ( _definition . MaxBufferSize . Value )
175+ {
176+ SingleReader = true ,
177+ FullMode = BoundedChannelFullMode . DropOldest
178+ } )
179+ : Channel . CreateUnbounded < ConsoleMessage > (
180+ new UnboundedChannelOptions ( )
181+ {
182+ SingleReader = true ,
183+ }
184+ ) ;
185+ }
135186 }
136187}
0 commit comments