|
29 | 29 | import warnings |
30 | 30 | from textwrap import indent |
31 | 31 | from threading import Thread |
| 32 | +from types import FrameType |
32 | 33 | from typing import ( |
33 | 34 | TYPE_CHECKING, |
34 | 35 | Any, |
35 | 36 | Awaitable, |
36 | 37 | Callable, |
37 | 38 | NoReturn, |
38 | 39 | Optional, |
| 40 | + Union, |
39 | 41 | cast, |
40 | 42 | ) |
41 | 43 | from wsgiref.simple_server import WSGIServer |
|
72 | 74 | from synapse.http.site import SynapseSite |
73 | 75 | from synapse.logging.context import LoggingContext, PreserveLoggingContext |
74 | 76 | from synapse.metrics import install_gc_manager, register_threadpool |
75 | | -from synapse.metrics.background_process_metrics import run_as_background_process |
76 | 77 | from synapse.metrics.jemalloc import setup_jemalloc_stats |
77 | 78 | from synapse.module_api.callbacks.spamchecker_callbacks import load_legacy_spam_checkers |
78 | 79 | from synapse.module_api.callbacks.third_party_event_rules_callbacks import ( |
@@ -540,6 +541,59 @@ def refresh_certificate(hs: "HomeServer") -> None: |
540 | 541 | logger.info("Context factories updated.") |
541 | 542 |
|
542 | 543 |
|
| 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 | + |
543 | 597 | async def start(hs: "HomeServer", freeze: bool = True) -> None: |
544 | 598 | """ |
545 | 599 | Start a Synapse server or worker. |
@@ -579,45 +633,9 @@ async def start(hs: "HomeServer", freeze: bool = True) -> None: |
579 | 633 | name="gai_resolver", server_name=server_name, threadpool=resolver_threadpool |
580 | 634 | ) |
581 | 635 |
|
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) |
621 | 639 |
|
622 | 640 | # Apply the cache config. |
623 | 641 | hs.config.caches.resize_all_caches() |
|
0 commit comments