@@ -117,8 +117,11 @@ func prepareCmd(ctx context.Context, dir string, env []string, name string, args
117117 }
118118 // Use SIGINT instead of SIGKILL for graceful shutdown of subprocesses
119119 cmd .Cancel = func () error {
120+ logger .DebugContext (ctx , "SIGINT sent to command" , slog .String ("cmd" , name ), slog .Any ("args" , args ))
120121 return cmd .Process .Signal (syscall .SIGINT )
121122 }
123+ // Ensure it eventually dies if it ignores SIGINT
124+ cmd .WaitDelay = shutdownTimeout / 2
122125
123126 return cmd
124127}
@@ -369,8 +372,15 @@ func gitHandler(w http.ResponseWriter, r *http.Request) {
369372 }
370373 }
371374
372- semaphore <- struct {}{}
373- defer func () { <- semaphore }()
375+ select {
376+ case semaphore <- struct {}{}:
377+ defer func () { <- semaphore }()
378+ case <- ctx .Done ():
379+ logger .WarnContext (ctx , "Request cancelled while waiting for semaphore" )
380+ http .Error (w , "Server context cancelled" , http .StatusServiceUnavailable )
381+
382+ return
383+ }
374384 logger .DebugContext (ctx , "Concurrent requests" , slog .Int ("count" , len (semaphore )))
375385
376386 // Fetch repo first
@@ -441,8 +451,15 @@ func cacheHandler(w http.ResponseWriter, r *http.Request) {
441451 ctx := context .WithValue (r .Context (), urlKey , url )
442452 logger .InfoContext (ctx , "Received request: /cache" )
443453
444- semaphore <- struct {}{}
445- defer func () { <- semaphore }()
454+ select {
455+ case semaphore <- struct {}{}:
456+ defer func () { <- semaphore }()
457+ case <- ctx .Done ():
458+ logger .WarnContext (ctx , "Request cancelled while waiting for semaphore" )
459+ http .Error (w , "Server context cancelled" , http .StatusServiceUnavailable )
460+
461+ return
462+ }
446463 logger .DebugContext (ctx , "Concurrent requests" , slog .Int ("count" , len (semaphore )))
447464
448465 // Fetch repo if it's not fresh
0 commit comments