@@ -190,34 +190,36 @@ func (p *PIDZero) Run() error {
190190 // Start a single reload manager if any runnable is reloadable
191191 for _ , r := range p .runnables {
192192 if _ , ok := r .(Reloadable ); ok {
193- p .wg .Add (1 )
194- go p .startReloadManager ()
193+ p .wg .Go (p .startReloadManager )
195194 break
196195 }
197196 }
198197
199198 // Start a single state monitor if any runnable reports state
200199 for _ , r := range p .runnables {
201200 if _ , ok := r .(Stateable ); ok {
202- p .wg .Add (1 )
203- go p .startStateMonitor ()
201+ p .wg .Go (p .startStateMonitor )
204202 break
205203 }
206204 }
207205
208206 // Start a single shutdown manager if any runnable can trigger shutdown
209207 for _ , r := range p .runnables {
210208 if _ , ok := r .(ShutdownSender ); ok {
211- p .wg .Add (1 )
212- go p .startShutdownManager ()
209+ p .wg .Go (p .startShutdownManager )
213210 break
214211 }
215212 }
216213
217214 // Start each service in sequence
218215 for _ , r := range p .runnables {
219- p .wg .Add (1 )
220- go p .startRunnable (r ) // start this runnable in a separate goroutine
216+ p .wg .Go (func () {
217+ err := p .startRunnable (r )
218+ if err != nil {
219+ p .logger .Error ("Runnable exited with error" , "runnable" , r , "error" , err )
220+ p .errorChan <- err
221+ }
222+ })
221223
222224 // if this Runnable implements the Stateable block here until IsRunning()
223225 if stateable , ok := r .(Stateable ); ok {
@@ -263,9 +265,7 @@ func (p *PIDZero) blockUntilRunnableReady(r Stateable) error {
263265 )
264266 select {
265267 case err := <- p .errorChan :
266- // error received from `startRunnable`- put it back in the channel for reap() to process it later
267- p .errorChan <- err
268- return fmt .Errorf ("runnable failed to start: %w" , err )
268+ return err
269269 case <- startupCtx .Done ():
270270 return fmt .Errorf ("timeout waiting for runnable to start: %w" , startupCtx .Err ())
271271 case <- p .ctx .Done ():
@@ -362,9 +362,7 @@ func (p *PIDZero) listenForSignals() {
362362}
363363
364364// startRunnable starts a service and sends any errors to the error channel
365- func (p * PIDZero ) startRunnable (r Runnable ) {
366- defer p .wg .Done ()
367-
365+ func (p * PIDZero ) startRunnable (r Runnable ) error {
368366 // Log the initial state if available
369367 if stateable , ok := r .(Stateable ); ok {
370368 initialState := stateable .GetState ()
@@ -378,31 +376,16 @@ func (p *PIDZero) startRunnable(r Runnable) {
378376
379377 // Run the runnable with the child context
380378 err := r .Run (runCtx )
381- logger := p .logger .With ("runnable" , r )
382- if err == nil {
383- logger .Debug ("Runnable completed without error" )
384- return
385- }
386- if errors .Is (err , context .Canceled ) || errors .Is (err , context .DeadlineExceeded ) {
387- // Filter out expected cancellation errors
388- logger .Debug ("Runnable stopped gracefully" , "reason" , err )
389- return
390- }
391-
392- // Unexpected error - log and send to the errorChan
393- logger = logger .With ("error" , err )
394- if stateable , ok := r .(Stateable ); ok {
395- logger .Error ("Service failed" , "state" , stateable .GetState ())
396- } else {
397- logger .Error ("Service failed" )
398- }
379+ if err != nil {
380+ if errors .Is (err , context .Canceled ) || errors .Is (err , context .DeadlineExceeded ) {
381+ // Filter out expected cancellation errors
382+ p .logger .Debug ("Runnable stopped gracefully" , "reason" , err , "runnable" , r )
383+ return nil
384+ }
399385
400- select {
401- case p .errorChan <- fmt .Errorf ("failed to start runnable: %w" , err ):
402- // Error sent successfully
403- default :
404- logger .Warn ("Unable to send error to errorChan (full or closed?)" )
386+ return err
405387 }
388+ return nil
406389}
407390
408391// reap listens indefinitely for errors or OS signals and handles them appropriately.
0 commit comments