2929import warnings
3030from textwrap import indent
3131from threading import Thread
32+ from types import FrameType
3233from typing import (
3334 TYPE_CHECKING ,
3435 Any ,
3536 Awaitable ,
3637 Callable ,
3738 NoReturn ,
3839 Optional ,
40+ Union ,
3941 cast ,
4042)
4143from wsgiref .simple_server import WSGIServer
7274from synapse .http .site import SynapseSite
7375from synapse .logging .context import LoggingContext , PreserveLoggingContext
7476from synapse .metrics import install_gc_manager , register_threadpool
75- from synapse .metrics .background_process_metrics import run_as_background_process
7677from synapse .metrics .jemalloc import setup_jemalloc_stats
7778from synapse .module_api .callbacks .spamchecker_callbacks import load_legacy_spam_checkers
7879from synapse .module_api .callbacks .third_party_event_rules_callbacks import (
108109
109110
110111def register_sighup (
111- homeserver_instance_id : str ,
112+ hs : "HomeServer" ,
112113 func : Callable [P , None ],
113114 * args : P .args ,
114115 ** kwargs : P .kwargs ,
@@ -123,19 +124,25 @@ def register_sighup(
123124 *args, **kwargs: args and kwargs to be passed to the target function.
124125 """
125126
126- _instance_id_to_sighup_callbacks_map .setdefault (homeserver_instance_id , []).append (
127- (func , args , kwargs )
127+ # Wrap the function so we can run it within a logcontext
128+ def _callback_wrapper (* args : P .args , ** kwargs : P .kwargs ) -> None :
129+ with LoggingContext (name = "sighup" , server_name = hs .hostname ):
130+ func (* args , ** kwargs )
131+
132+ _instance_id_to_sighup_callbacks_map .setdefault (hs .get_instance_id (), []).append (
133+ (_callback_wrapper , args , kwargs )
128134 )
129135
130136
131- def unregister_sighups (instance_id : str ) -> None :
137+ def unregister_sighups (homeserver_instance_id : str ) -> None :
132138 """
133139 Unregister all sighup functions associated with this Synapse instance.
134140
135141 Args:
136- instance_id: Unique ID for this Synapse process instance.
142+ homeserver_instance_id: The unique ID for this Synapse process instance to
143+ unregister hooks for (`hs.get_instance_id()`).
137144 """
138- _instance_id_to_sighup_callbacks_map .pop (instance_id , [])
145+ _instance_id_to_sighup_callbacks_map .pop (homeserver_instance_id , [])
139146
140147
141148def start_worker_reactor (
@@ -540,6 +547,61 @@ def refresh_certificate(hs: "HomeServer") -> None:
540547 logger .info ("Context factories updated." )
541548
542549
550+ _already_setup_sighup_handling = False
551+ """
552+ Marks whether we've already successfully ran `setup_sighup_handling()`.
553+ """
554+
555+
556+ def setup_sighup_handling () -> None :
557+ """
558+ Set up SIGHUP handling to call registered callbacks.
559+
560+ This can be called multiple times safely.
561+ """
562+ global _already_setup_sighup_handling
563+ # We only need to set things up once per process.
564+ if _already_setup_sighup_handling :
565+ return
566+
567+ previous_sighup_handler : Union [
568+ Callable [[int , Optional [FrameType ]], Any ], int , None
569+ ] = None
570+
571+ # Set up the SIGHUP machinery.
572+ if hasattr (signal , "SIGHUP" ):
573+
574+ def handle_sighup (* args : Any , ** kwargs : Any ) -> None :
575+ # Tell systemd our state, if we're using it. This will silently fail if
576+ # we're not using systemd.
577+ sdnotify (b"RELOADING=1" )
578+
579+ if callable (previous_sighup_handler ):
580+ previous_sighup_handler (* args , ** kwargs )
581+
582+ for sighup_callbacks in _instance_id_to_sighup_callbacks_map .values ():
583+ for func , args , kwargs in sighup_callbacks :
584+ func (* args , ** kwargs )
585+
586+ sdnotify (b"READY=1" )
587+
588+ # We defer running the sighup handlers until next reactor tick. This
589+ # is so that we're in a sane state, e.g. flushing the logs may fail
590+ # if the sighup happens in the middle of writing a log entry.
591+ def run_sighup (* args : Any , ** kwargs : Any ) -> None :
592+ # `callFromThread` should be "signal safe" as well as thread
593+ # safe.
594+ reactor .callFromThread (handle_sighup , * args , ** kwargs )
595+
596+ # Register for the SIGHUP signal, chaining any existing handler as there can
597+ # only be one handler per signal and we don't want to clobber any existing
598+ # handlers (like the `multi_synapse` shard process in the context of Synapse Pro
599+ # for small hosts)
600+ previous_sighup_handler = signal .signal (signal .SIGHUP , run_sighup )
601+
602+ _already_setup_sighup_handling = True
603+
604+
543605async def start (hs : "HomeServer" , freeze : bool = True ) -> None :
544606 """
545607 Start a Synapse server or worker.
@@ -579,45 +641,9 @@ async def start(hs: "HomeServer", freeze: bool = True) -> None:
579641 name = "gai_resolver" , server_name = server_name , threadpool = resolver_threadpool
580642 )
581643
582- # Set up the SIGHUP machinery.
583- if hasattr (signal , "SIGHUP" ):
584-
585- def handle_sighup (* args : Any , ** kwargs : Any ) -> "defer.Deferred[None]" :
586- async def _handle_sighup (* args : Any , ** kwargs : Any ) -> None :
587- # Tell systemd our state, if we're using it. This will silently fail if
588- # we're not using systemd.
589- sdnotify (b"RELOADING=1" )
590-
591- for sighup_callbacks in _instance_id_to_sighup_callbacks_map .values ():
592- for func , args , kwargs in sighup_callbacks :
593- func (* args , ** kwargs )
594-
595- sdnotify (b"READY=1" )
596-
597- # It's okay to ignore the linter error here and call
598- # `run_as_background_process` directly because `_handle_sighup` operates
599- # outside of the scope of a specific `HomeServer` instance and holds no
600- # references to it which would prevent a clean shutdown.
601- return run_as_background_process ( # type: ignore[untracked-background-process]
602- "sighup" ,
603- server_name ,
604- _handle_sighup ,
605- * args ,
606- ** kwargs ,
607- )
608-
609- # We defer running the sighup handlers until next reactor tick. This
610- # is so that we're in a sane state, e.g. flushing the logs may fail
611- # if the sighup happens in the middle of writing a log entry.
612- def run_sighup (* args : Any , ** kwargs : Any ) -> None :
613- # `callFromThread` should be "signal safe" as well as thread
614- # safe.
615- reactor .callFromThread (handle_sighup , * args , ** kwargs )
616-
617- signal .signal (signal .SIGHUP , run_sighup )
618-
619- register_sighup (hs .get_instance_id (), refresh_certificate , hs )
620- register_sighup (hs .get_instance_id (), reload_cache_config , hs .config )
644+ setup_sighup_handling ()
645+ register_sighup (hs , refresh_certificate , hs )
646+ register_sighup (hs , reload_cache_config , hs .config )
621647
622648 # Apply the cache config.
623649 hs .config .caches .resize_all_caches ()
0 commit comments