@@ -192,37 +192,45 @@ public async Task Stop(CancellationToken cancellationToken)
192192
193193 await shutdownTokenSource . CancelAsync ( ) ;
194194
195- // EmbeddedServer does not have have an async Stop method, the Dispose operation blocks and waits
196- // until its GracefulShutdownTimeout is reached and then does a Process.Kill
195+ // This is a workaround until the EmbeddedServer properly supports cancellation!
197196 //
198- // This logic gets the child process ID uses a Task.Delay with infinite.
197+ // EmbeddedServer does not have an async Stop method, the Dispose operation blocks and waits
198+ // until its GracefulShutdownTimeout is reached and then does a Process.Kill. Due to this behavior this can
199+ // be shorter or longer than the allowed stop duration.
200+ //
201+ // With the Task.WhenAny 2 things can happen:
199202 //
200203 // a. The Task.Delay gets cancelled first
201204 // b. The EmbeddedServer.Dispose completes first
202205 //
203206 // If the Task.Delay gets cancelled first this means Dispose is still running and
204- // then we kill the process
207+ // then we try and kill the process
205208
206209 serverOptions ! . GracefulShutdownTimeout = TimeSpan . FromHours ( 1 ) ; // During Stop/Dispose we manually control this
207210
208- var processId = await EmbeddedServer . Instance . GetServerProcessIdAsync ( cancellationToken ) ;
209211
210212 // Dispose always need to be invoked, even when already cancelled
211213 var disposeTask = Task . Run ( ( ) => EmbeddedServer . Instance . Dispose ( ) , CancellationToken . None ) ;
212214
215+ // Runs "infinite" but will eventually get cancelled
213216 var waitForCancellationTask = Task . Delay ( Timeout . Infinite , cancellationToken ) ;
214217
215218 var delayIsCancelled = waitForCancellationTask == await Task . WhenAny ( disposeTask , waitForCancellationTask ) ;
216219
217220 if ( delayIsCancelled )
218221 {
222+ int processId = 0 ;
219223 try
220224 {
225+ // We always want to try and kill the process, even when already cancelled
226+ processId = await EmbeddedServer . Instance . GetServerProcessIdAsync ( CancellationToken . None ) ;
221227 Logger . WarnFormat ( "Killing RavenDB server PID {0} because host cancelled" , processId ) ;
222228 using var ravenChildProcess = Process . GetProcessById ( processId ) ;
223229 ravenChildProcess . Kill ( entireProcessTree : true ) ;
224230 // Kill only signals
225231 Logger . WarnFormat ( "Waiting for RavenDB server PID {0} to exit... " , processId ) ;
232+ // When WaitForExitAsync returns, the process could still exist but in a frozen state to flush
233+ // memory mapped pages to storage.
226234 await ravenChildProcess . WaitForExitAsync ( CancellationToken . None ) ;
227235 }
228236 catch ( Exception e )
0 commit comments