11import importlib
22import json
3+ import multiprocessing
34import os
45import platform
56import signal
67import subprocess
78import sys
8- import threading
99import time
1010import tomllib
1111from importlib .metadata import entry_points
@@ -223,21 +223,16 @@ def run(self):
223223 # If plain.models is installed (common) then we
224224 # will do a couple extra things before starting all of the app-related
225225 # processes (this way they don't all have to db-wait or anything)
226+ process = None
226227 if find_spec ("plain.models" ) is not None :
227228 # Use a custom signal to tell the main thread to add
228229 # the app processes once the db is ready
229230 signal .signal (signal .SIGUSR1 , self .start_app )
230231
231- def _thread (env ):
232- subprocess .run (["plain" , "models" , "db-wait" ], env = env , check = True )
233- subprocess .run (["plain" , "migrate" , "--backup" ], env = env , check = True )
234- # preflight with db?
235- os .kill (os .getpid (), signal .SIGUSR1 )
236-
237- thread = threading .Thread (
238- target = _thread , daemon = True , args = (self .plain_env ,)
232+ process = multiprocessing .Process (
233+ target = _process_task , args = (self .plain_env ,)
239234 )
240- thread .start ()
235+ process .start ()
241236 else :
242237 # Start the app processes immediately
243238 self .start_app (None , None )
@@ -253,6 +248,14 @@ def _thread(env):
253248 if services_pid :
254249 services_pid .rm ()
255250
251+ # Make sure the process is terminated if it is still running
252+ if process and process .is_alive ():
253+ os .killpg (os .getpgid (process .pid ), signal .SIGTERM )
254+ process .join (timeout = 3 )
255+ if process .is_alive ():
256+ os .killpg (os .getpgid (process .pid ), signal .SIGKILL )
257+ process .join ()
258+
256259 return self .poncho .returncode
257260
258261 def start_app (self , signum , frame ):
@@ -439,3 +442,16 @@ def add_pyproject_run(self):
439442 ** data .get ("env" , {}),
440443 }
441444 self .poncho .add_process (name , data ["cmd" ], env = env )
445+
446+
447+ def _process_task (env ):
448+ # Make this process the leader of a new group which can be killed together if it doesn't finish
449+ os .setsid ()
450+
451+ subprocess .run (["plain" , "models" , "db-wait" ], env = env , check = True )
452+ subprocess .run (["plain" , "migrate" , "--backup" ], env = env , check = True )
453+
454+ # preflight with db?
455+
456+ # Send SIGUSR1 to the parent process so the parent's handler is invoked
457+ os .kill (os .getppid (), signal .SIGUSR1 )
0 commit comments