Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set multiprocessing start method early #2776

Merged
merged 12 commits into from
Jul 9, 2023
5 changes: 3 additions & 2 deletions sanic/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
AnyStr,
Awaitable,
Callable,
ClassVar,
Coroutine,
Deque,
Dict,
Expand Down Expand Up @@ -158,8 +159,8 @@ class Sanic(StaticHandleMixin, BaseSanic, StartupMixin, metaclass=TouchUpMeta):
"websocket_tasks",
)

_app_registry: Dict[str, "Sanic"] = {}
test_mode = False
_app_registry: ClassVar[Dict[str, "Sanic"]] = {}
test_mode: ClassVar[bool] = False

def __init__(
self,
Expand Down
35 changes: 31 additions & 4 deletions sanic/mixins/startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,13 @@
from contextlib import suppress
from functools import partial
from importlib import import_module
from multiprocessing import Manager, Pipe, get_context
from multiprocessing import (
Manager,
Pipe,
get_context,
get_start_method,
set_start_method,
)
from multiprocessing.context import BaseContext
from pathlib import Path
from socket import SHUT_RDWR, socket
Expand All @@ -25,6 +31,7 @@
TYPE_CHECKING,
Any,
Callable,
ClassVar,
Dict,
List,
Mapping,
Expand Down Expand Up @@ -81,13 +88,17 @@


class StartupMixin(metaclass=SanicMeta):
_app_registry: Dict[str, Sanic]
_app_registry: ClassVar[Dict[str, Sanic]]

config: Config
listeners: Dict[str, List[ListenerType[Any]]]
state: ApplicationState
websocket_enabled: bool
multiplexer: WorkerMultiplexer
start_method: StartMethod = _default

test_mode: ClassVar[bool]
start_method: ClassVar[StartMethod] = _default
START_METHOD_SET: ClassVar[bool] = False

def setup_loop(self):
if not self.asgi:
Expand Down Expand Up @@ -691,11 +702,26 @@ def _get_startup_method(cls) -> str:
else "spawn"
)

@classmethod
def _set_startup_method(cls) -> None:
if cls.START_METHOD_SET and not cls.test_mode:
return

method = cls._get_startup_method()
set_start_method(method, force=cls.test_mode)
cls.START_METHOD_SET = True

@classmethod
def _get_context(cls) -> BaseContext:
method = cls._get_startup_method()
logger.debug("Creating multiprocessing context using '%s'", method)
return get_context(method)
actual = get_start_method()
if method != actual:
raise RuntimeError(
f"Start method '{method}' was requested, but '{actual}' "
"was actually set."
)
return get_context()

@classmethod
def serve(
Expand All @@ -705,6 +731,7 @@ def serve(
app_loader: Optional[AppLoader] = None,
factory: Optional[Callable[[], Sanic]] = None,
) -> None:
cls._set_startup_method()
os.environ["SANIC_MOTD_OUTPUT"] = "true"
apps = list(cls._app_registry.values())
if factory:
Expand Down
1 change: 1 addition & 0 deletions tests/test_errorpages.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pytest

import sanic

from sanic import Sanic
from sanic.config import Config
from sanic.errorpages import TextRenderer, exception_response, guess_mime
Expand Down