Skip to content

Commit 8567485

Browse files
committed
Be mindful of other SIGHUP handlers
1 parent b835eb2 commit 8567485

File tree

1 file changed

+58
-40
lines changed

1 file changed

+58
-40
lines changed

synapse/app/_base.py

Lines changed: 58 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929
import warnings
3030
from textwrap import indent
3131
from threading import Thread
32+
from types import FrameType
3233
from typing import (
3334
TYPE_CHECKING,
3435
Any,
3536
Awaitable,
3637
Callable,
3738
NoReturn,
3839
Optional,
40+
Union,
3941
cast,
4042
)
4143
from wsgiref.simple_server import WSGIServer
@@ -72,7 +74,6 @@
7274
from synapse.http.site import SynapseSite
7375
from synapse.logging.context import LoggingContext, PreserveLoggingContext
7476
from synapse.metrics import install_gc_manager, register_threadpool
75-
from synapse.metrics.background_process_metrics import run_as_background_process
7677
from synapse.metrics.jemalloc import setup_jemalloc_stats
7778
from synapse.module_api.callbacks.spamchecker_callbacks import load_legacy_spam_checkers
7879
from synapse.module_api.callbacks.third_party_event_rules_callbacks import (
@@ -540,6 +541,59 @@ def refresh_certificate(hs: "HomeServer") -> None:
540541
logger.info("Context factories updated.")
541542

542543

544+
_already_setup_sighup_handling = False
545+
"""
546+
Marks whether we've already successfully ran `setup_sighup_handling()`.
547+
"""
548+
549+
550+
def setup_sighup_handling() -> None:
551+
"""
552+
Set up SIGHUP handling to call registered callbacks.
553+
554+
This can be called multiple times safely.
555+
"""
556+
global _already_setup_sighup_handling
557+
# We only need to set things up once per process.
558+
if _already_setup_sighup_handling:
559+
return
560+
561+
previous_sighup_handler: Union[
562+
Callable[[int, Optional[FrameType]], Any], int, None
563+
] = None
564+
565+
# Set up the SIGHUP machinery.
566+
if hasattr(signal, "SIGHUP"):
567+
568+
def handle_sighup(*args: Any, **kwargs: Any) -> None:
569+
# Tell systemd our state, if we're using it. This will silently fail if
570+
# we're not using systemd.
571+
sdnotify(b"RELOADING=1")
572+
573+
if callable(previous_sighup_handler):
574+
previous_sighup_handler(*args, **kwargs)
575+
576+
for sighup_callbacks in _instance_id_to_sighup_callbacks_map.values():
577+
for func, args, kwargs in sighup_callbacks:
578+
func(*args, **kwargs)
579+
580+
sdnotify(b"READY=1")
581+
582+
# We defer running the sighup handlers until next reactor tick. This
583+
# is so that we're in a sane state, e.g. flushing the logs may fail
584+
# if the sighup happens in the middle of writing a log entry.
585+
def run_sighup(*args: Any, **kwargs: Any) -> None:
586+
# `callFromThread` should be "signal safe" as well as thread
587+
# safe.
588+
reactor.callFromThread(handle_sighup, *args, **kwargs)
589+
590+
# Register for the SIGHUP signal, chaining any existing handler as there can
591+
# only be one handler per signal and we don't want to clobber any existing
592+
# handlers (like the `multi_synapse` shard process in the context of Synapse Pro
593+
# for small hosts)
594+
previous_sighup_handler = signal.signal(signal.SIGHUP, run_sighup)
595+
596+
543597
async def start(hs: "HomeServer", freeze: bool = True) -> None:
544598
"""
545599
Start a Synapse server or worker.
@@ -579,45 +633,9 @@ async def start(hs: "HomeServer", freeze: bool = True) -> None:
579633
name="gai_resolver", server_name=server_name, threadpool=resolver_threadpool
580634
)
581635

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)
636+
setup_sighup_handling()
637+
register_sighup(hs.get_instance_id(), refresh_certificate, hs)
638+
register_sighup(hs.get_instance_id(), reload_cache_config, hs.config)
621639

622640
# Apply the cache config.
623641
hs.config.caches.resize_all_caches()

0 commit comments

Comments
 (0)