2222from .user_app_loader import load_user_app
2323from .runtime import execution_context
2424import logging
25+ import multiprocessing as mp
2526
2627WATCHDOG_INTERVAL_SECONDS = 10.0
2728
@@ -43,6 +44,7 @@ def _get_pool() -> ProcessPoolExecutor:
4344 max_workers = 1 ,
4445 initializer = _subprocess_init ,
4546 initargs = (_user_apps , os .getpid ()),
47+ mp_context = mp .get_context ("spawn" ),
4648 )
4749 return _pool
4850
@@ -69,6 +71,7 @@ def _restart_pool(old_pool: ProcessPoolExecutor | None = None) -> None:
6971 max_workers = 1 ,
7072 initializer = _subprocess_init ,
7173 initargs = (_user_apps , os .getpid ()),
74+ mp_context = mp .get_context ("spawn" ),
7275 )
7376 if prev_pool is not None :
7477 # Best-effort shutdown of previous pool; letting exceptions bubble up
@@ -124,8 +127,19 @@ def _watch() -> None:
124127
125128def _subprocess_init (user_apps : list [str ], parent_pid : int ) -> None :
126129 _start_parent_watchdog (parent_pid )
130+
131+ # In case any user app is already in this subprocess, e.g. the subprocess is forked, we need to avoid loading it again.
132+ with _pool_lock :
133+ already_loaded_apps = set (_user_apps )
134+
135+ loaded_apps = []
127136 for app_target in user_apps :
128- load_user_app (app_target )
137+ if app_target not in already_loaded_apps :
138+ load_user_app (app_target )
139+ loaded_apps .append (app_target )
140+
141+ with _pool_lock :
142+ _user_apps .extend (loaded_apps )
129143
130144
131145class _OnceResult :
0 commit comments