@@ -88,7 +88,13 @@ def launch_window(self, window_name, url, monitor, index):
8888 print (f"[Chromium] Launching '{ window_name } ' on monitor { index } "
8989 f"({ monitor .width } x{ monitor .height } at { monitor .x } ,{ monitor .y } )" )
9090
91- proc = subprocess .Popen (args , env = env )
91+ # Launch in its own process group so we can kill the entire tree on shutdown
92+ # (Chromium spawns renderers, GPU, zygote children that must all be killed)
93+ popen_kwargs = dict (env = env )
94+ if platform .system () != "Windows" :
95+ popen_kwargs ['start_new_session' ] = True
96+
97+ proc = subprocess .Popen (args , ** popen_kwargs )
9298 self ._processes .append ((window_name , proc , user_data_dir ))
9399 return proc
94100
@@ -128,16 +134,33 @@ def launch_all_windows(self, iniconfig, base_url="http://127.0.0.1"):
128134
129135 print (f"[Chromium] Launched { len (self ._processes )} browser windows" )
130136
137+ def _kill_process_tree (self , proc , window_name , force = False ):
138+ """Kill a Chromium process and all its children (renderers, GPU, zygote)."""
139+ if platform .system () == "Windows" :
140+ # taskkill /T kills the entire process tree, /F forces it
141+ try :
142+ subprocess .call (
143+ ['taskkill' , '/F' , '/T' , '/PID' , str (proc .pid )],
144+ stdout = subprocess .DEVNULL , stderr = subprocess .DEVNULL
145+ )
146+ except Exception as e :
147+ print (f"[Chromium] taskkill failed for '{ window_name } ': { e } " )
148+ else :
149+ sig = signal .SIGKILL if force else signal .SIGTERM
150+ try :
151+ os .killpg (proc .pid , sig )
152+ except ProcessLookupError :
153+ pass
154+ except Exception as e :
155+ print (f"[Chromium] killpg failed for '{ window_name } ': { e } " )
156+
131157 def terminate_all (self ):
132158 """Terminate all Chromium processes gracefully."""
133159 print ("[Chromium] Terminating all browser windows..." )
134160 for window_name , proc , temp_dir in self ._processes :
135161 try :
136162 if proc .poll () is None : # still running
137- if platform .system () == "Windows" :
138- proc .terminate ()
139- else :
140- proc .send_signal (signal .SIGTERM )
163+ self ._kill_process_tree (proc , window_name )
141164 except Exception as e :
142165 print (f"[Chromium] Error terminating '{ window_name } ': { e } " )
143166
@@ -147,7 +170,7 @@ def terminate_all(self):
147170 proc .wait (timeout = 5 )
148171 except subprocess .TimeoutExpired :
149172 print (f"[Chromium] Force killing '{ window_name } '" )
150- proc . kill ( )
173+ self . _kill_process_tree ( proc , window_name , force = True )
151174
152175 self ._processes .clear ()
153176 self ._exit_event .set ()
0 commit comments