@@ -179,10 +179,11 @@ service.Description.RunInfo is ProjectRunInfo project2 &&
179179 }
180180
181181 private void LaunchService ( Application application , Service service )
182+
182183 {
183- var serviceDescription = service . Description ;
184- var processInfo = new ProcessInfo ( new Task [ service . Description . Replicas ] ) ;
185- var serviceName = serviceDescription . Name ;
184+ var processInfo = ( service . Items . ContainsKey ( typeof ( ProcessInfo ) ) ? ( ProcessInfo ? ) service . Items [ typeof ( ProcessInfo ) ] : null )
185+ ?? new ProcessInfo ( new Task [ service . Description . Replicas ] ) ;
186+ var serviceName = service . Description . Name ;
186187
187188 // Set by BuildAndRunService
188189 var args = service . Status . Args ! ;
@@ -258,7 +259,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
258259
259260 var backOff = TimeSpan . FromSeconds ( 5 ) ;
260261
261- while ( ! processInfo . StoppedTokenSource . IsCancellationRequested )
262+ while ( ! processInfo ! . StoppedTokenSource . IsCancellationRequested )
262263 {
263264 var replica = serviceName + "_" + Guid . NewGuid ( ) . ToString ( ) . Substring ( 0 , 10 ) . ToLower ( ) ;
264265 var status = new ProcessStatus ( service , replica ) ;
@@ -297,7 +298,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
297298 try
298299 {
299300 service . Logs . OnNext ( $ "[{ replica } ]:{ path } { copiedArgs } ") ;
300- var processInfo = new ProcessSpec
301+ var processSpec = new ProcessSpec
301302 {
302303 Executable = path ,
303304 WorkingDirectory = workingDirectory ,
@@ -351,8 +352,10 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
351352 // Only increase backoff when not watching project as watch will wait for file changes before rebuild.
352353 backOff *= 2 ;
353354 }
354-
355- service . Restarts ++ ;
355+ if ( ! processInfo . StoppedTokenSource . IsCancellationRequested )
356+ {
357+ service . Restarts ++ ;
358+ }
356359
357360 service . Replicas . TryRemove ( replica , out var _ ) ;
358361 service . ReplicaEvents . OnNext ( new ReplicaEvent ( ReplicaState . Removed , status ) ) ;
@@ -385,7 +388,7 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
385388 environment [ "DOTNET_WATCH" ] = "1" ;
386389
387390 await new DotNetWatcher ( _logger )
388- . WatchAsync ( processInfo , fileSetFactory , replica , status . StoppingTokenSource . Token ) ;
391+ . WatchAsync ( processSpec , fileSetFactory , replica , status . StoppingTokenSource . Token ) ;
389392 }
390393 else if ( _options . Watch && ( service . Description . RunInfo is AzureFunctionRunInfo azureFunctionRunInfo ) && ! string . IsNullOrEmpty ( azureFunctionRunInfo . ProjectFile ) )
391394 {
@@ -397,11 +400,11 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
397400 environment [ "DOTNET_WATCH" ] = "1" ;
398401
399402 await new DotNetWatcher ( _logger )
400- . WatchAsync ( processInfo , fileSetFactory , replica , status . StoppingTokenSource . Token ) ;
403+ . WatchAsync ( processSpec , fileSetFactory , replica , status . StoppingTokenSource . Token ) ;
401404 }
402405 else
403406 {
404- await ProcessUtil . RunAsync ( processInfo , status . StoppingTokenSource . Token , throwOnError : false ) ;
407+ await ProcessUtil . RunAsync ( processSpec , status . StoppingTokenSource . Token , throwOnError : false ) ;
405408 }
406409 }
407410 catch ( Exception ex )
@@ -429,50 +432,77 @@ async Task RunApplicationAsync(IEnumerable<(int ExternalPort, int Port, string?
429432 }
430433 }
431434
432- if ( serviceDescription . Bindings . Count > 0 )
435+ void Start ( )
433436 {
434- // Each replica is assigned a list of internal ports, one mapped to each external
435- // port
436- for ( int i = 0 ; i < serviceDescription . Replicas ; i ++ )
437+ if ( service . Description ! . Bindings . Count > 0 )
437438 {
438- var ports = new List < ( int , int , string ? , string ? ) > ( ) ;
439- foreach ( var binding in serviceDescription . Bindings )
439+ // Each replica is assigned a list of internal ports, one mapped to each external
440+ // port
441+ for ( int i = 0 ; i < service . Description . Replicas ; i ++ )
440442 {
441- if ( binding . Port == null )
443+ var ports = new List < ( int , int , string ? , string ? ) > ( ) ;
444+ foreach ( var binding in service . Description . Bindings )
442445 {
443- continue ;
446+ if ( binding . Port == null )
447+ {
448+ continue ;
449+ }
450+
451+ ports . Add ( ( binding . Port . Value , binding . ReplicaPorts [ i ] , binding . Protocol , binding . Host ) ) ;
444452 }
445453
446- ports . Add ( ( binding . Port . Value , binding . ReplicaPorts [ i ] , binding . Protocol , binding . Host ) ) ;
454+ processInfo ! . Tasks [ i ] = RunApplicationAsync ( ports , args ) ;
455+ }
456+ }
457+ else
458+ {
459+ for ( int i = 0 ; i < service . Description . Replicas ; i ++ )
460+ {
461+ processInfo ! . Tasks [ i ] = RunApplicationAsync ( Enumerable . Empty < ( int , int , string ? , string ? ) > ( ) , args ) ;
447462 }
448-
449- processInfo . Tasks [ i ] = RunApplicationAsync ( ports , args ) ;
450463 }
451464 }
465+
466+ processInfo . Start = Start ;
467+ service . Items [ typeof ( ProcessInfo ) ] = processInfo ;
468+ if ( ! _options . ManualStartServices && ! ( _options . ServicesNotToStart ? . Contains ( serviceName , StringComparer . OrdinalIgnoreCase ) ?? false ) )
469+ {
470+ processInfo . Start ( ) ;
471+ }
452472 else
453473 {
454- for ( int i = 0 ; i < service . Description . Replicas ; i ++ )
474+ for ( int i = 0 ; i < processInfo . Tasks . Length ; i ++ )
455475 {
456- processInfo . Tasks [ i ] = RunApplicationAsync ( Enumerable . Empty < ( int , int , string ? , string ? ) > ( ) , args ) ;
476+ processInfo . Tasks [ i ] = Task . CompletedTask ;
457477 }
458478 }
479+ }
459480
460- service . Items [ typeof ( ProcessInfo ) ] = processInfo ;
481+ public static async Task RestartService ( Service service )
482+ {
483+ if ( service . Items . TryGetValue ( typeof ( ProcessInfo ) , out var stateObj ) && stateObj is ProcessInfo state )
484+ {
485+ await KillProcessAsync ( service ) ;
486+ service . Restarts ++ ;
487+ state . Start ? . Invoke ( ) ;
488+ await Task . WhenAll ( state . Tasks ) ;
489+ }
461490 }
462491
463- private Task KillRunningProcesses ( IDictionary < string , Service > services )
492+ public static async Task KillProcessAsync ( Service service )
464493 {
465- static Task KillProcessAsync ( Service service )
494+ if ( service . Items . TryGetValue ( typeof ( ProcessInfo ) , out var stateObj ) && stateObj is ProcessInfo state )
466495 {
467- if ( service . Items . TryGetValue ( typeof ( ProcessInfo ) , out var stateObj ) && stateObj is ProcessInfo state )
468- {
469- // Cancel the token before stopping the process
470- state . StoppedTokenSource . Cancel ( ) ;
496+ // Cancel the token before stopping the process
497+ state . StoppedTokenSource ? . Cancel ( ) ;
471498
472- return Task . WhenAll ( state . Tasks ) ;
473- }
474- return Task . CompletedTask ;
499+ await Task . WhenAll ( state . Tasks ) ;
500+ state . ResetStoppedTokenSource ( ) ;
475501 }
502+ }
503+
504+ private Task KillRunningProcesses ( IDictionary < string , Service > services )
505+ {
476506
477507 var index = 0 ;
478508 var tasks = new Task [ services . Count ] ;
@@ -541,7 +571,13 @@ public ProcessInfo(Task[] tasks)
541571
542572 public Task [ ] Tasks { get ; }
543573
544- public CancellationTokenSource StoppedTokenSource { get ; } = new CancellationTokenSource ( ) ;
574+ public CancellationTokenSource StoppedTokenSource { get ; private set ; } = new CancellationTokenSource ( ) ;
575+ public Action ? Start { get ; internal set ; }
576+ internal void ResetStoppedTokenSource ( )
577+ {
578+ StoppedTokenSource . Dispose ( ) ;
579+ StoppedTokenSource = new CancellationTokenSource ( ) ;
580+ }
545581 }
546582
547583 private class ProjectGroup
0 commit comments