Skip to content

Commit 44994ec

Browse files
committed
Be mindful of other SIGHUP handlers
1 parent 3b8147f commit 44994ec

1 file changed

Lines changed: 60 additions & 40 deletions

File tree

synapse/app/_base.py

Lines changed: 60 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
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,
@@ -39,6 +40,7 @@
3940
NoReturn,
4041
Optional,
4142
Tuple,
43+
Union,
4244
cast,
4345
)
4446
from wsgiref.simple_server import WSGIServer
@@ -75,7 +77,6 @@
7577
from synapse.http.site import SynapseSite
7678
from synapse.logging.context import LoggingContext, PreserveLoggingContext
7779
from synapse.metrics import install_gc_manager, register_threadpool
78-
from synapse.metrics.background_process_metrics import run_as_background_process
7980
from synapse.metrics.jemalloc import setup_jemalloc_stats
8081
from synapse.module_api.callbacks.spamchecker_callbacks import load_legacy_spam_checkers
8182
from synapse.module_api.callbacks.third_party_event_rules_callbacks import (
@@ -543,6 +544,61 @@ def refresh_certificate(hs: "HomeServer") -> None:
543544
logger.info("Context factories updated.")
544545

545546

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

585-
# Set up the SIGHUP machinery.
586-
if hasattr(signal, "SIGHUP"):
587-
588-
def handle_sighup(*args: Any, **kwargs: Any) -> "defer.Deferred[None]":
589-
async def _handle_sighup(*args: Any, **kwargs: Any) -> None:
590-
# Tell systemd our state, if we're using it. This will silently fail if
591-
# we're not using systemd.
592-
sdnotify(b"RELOADING=1")
593-
594-
for sighup_callbacks in _instance_id_to_sighup_callbacks_map.values():
595-
for func, args, kwargs in sighup_callbacks:
596-
func(*args, **kwargs)
597-
598-
sdnotify(b"READY=1")
599-
600-
# It's okay to ignore the linter error here and call
601-
# `run_as_background_process` directly because `_handle_sighup` operates
602-
# outside of the scope of a specific `HomeServer` instance and holds no
603-
# references to it which would prevent a clean shutdown.
604-
return run_as_background_process( # type: ignore[untracked-background-process]
605-
"sighup",
606-
server_name,
607-
_handle_sighup,
608-
*args,
609-
**kwargs,
610-
)
611-
612-
# We defer running the sighup handlers until next reactor tick. This
613-
# is so that we're in a sane state, e.g. flushing the logs may fail
614-
# if the sighup happens in the middle of writing a log entry.
615-
def run_sighup(*args: Any, **kwargs: Any) -> None:
616-
# `callFromThread` should be "signal safe" as well as thread
617-
# safe.
618-
reactor.callFromThread(handle_sighup, *args, **kwargs)
619-
620-
signal.signal(signal.SIGHUP, run_sighup)
621-
622-
register_sighup(hs.get_instance_id(), refresh_certificate, hs)
623-
register_sighup(hs.get_instance_id(), reload_cache_config, hs.config)
641+
setup_sighup_handling()
642+
register_sighup(hs.get_instance_id(), refresh_certificate, hs)
643+
register_sighup(hs.get_instance_id(), reload_cache_config, hs.config)
624644

625645
# Apply the cache config.
626646
hs.config.caches.resize_all_caches()

0 commit comments

Comments
 (0)