1//===--- Shutdown.h - Unclean exit scenarios --------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// LSP specifies a protocol for shutting down: a `shutdown` request followed
10// by an `exit` notification. If this protocol is followed, clangd should
11// finish outstanding work and exit with code 0.
12//
13// The way this works in the happy case:
14// - when ClangdLSPServer gets `shutdown`, it sets a flag
15// - when ClangdLSPServer gets `exit`, it returns false to indicate end-of-LSP
16// - Transport::loop() returns with no error
17// - ClangdServer::run() checks the shutdown flag and returns with no error.
18// - we `return 0` from main()
19// - destructor of ClangdServer and other main()-locals runs.
20// This blocks until outstanding requests complete (results are ignored)
21// - global destructors run, such as fallback deletion of temporary files
22//
23// There are a number of things that can go wrong. Some are handled here, and
24// some elsewhere.
25// - `exit` notification with no `shutdown`:
26// ClangdServer::run() sees this and returns false, main() returns nonzero.
27// - stdin/stdout are closed
28// The Transport detects this while doing IO and returns an error from loop()
29// ClangdServer::run() logs a message and then returns false, etc
30// - a request thread gets stuck, so the ClangdServer destructor hangs.
31// Before returning from main(), we start a watchdog thread to abort() the
32// process if it takes too long to exit. See abortAfterTimeout().
33// - clangd crashes (e.g. segfault or assertion)
34// A fatal signal is sent (SEGV, ABRT, etc)
35// The installed signal handler prints a stack trace and exits.
36// - parent process goes away or tells us to shut down
37// A "graceful shutdown" signal is sent (TERM, HUP, etc).
38// The installed signal handler calls requestShutdown() which sets a flag.
39// The Transport IO is interrupted, and Transport::loop() checks the flag and
40// returns an error, etc.
41//
42//===----------------------------------------------------------------------===//
43#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_SHUTDOWN_H
44#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_SHUTDOWN_H
45
46#include <cerrno>
47#include <chrono>
48#include <type_traits>
49#include <utility>
50
51namespace clang {
52namespace clangd {
53
54/// Causes this process to crash if still running after Timeout.
55void abortAfterTimeout(std::chrono::seconds Timeout);
56
57/// Sets a flag to indicate that clangd was sent a shutdown signal, and the
58/// transport loop should exit at the next opportunity.
59/// If shutdown was already requested, aborts the process.
60/// This function is threadsafe and signal-safe.
61void requestShutdown();
62/// Checks whether requestShutdown() was called.
63/// This function is threadsafe and signal-safe.
64bool shutdownRequested();
65
66/// Retry an operation if it gets interrupted by a signal.
67/// This is like llvm::sys::RetryAfterSignal, except that if shutdown was
68/// requested (which interrupts IO), we'll fail rather than retry.
69template <typename Fun, typename Ret = decltype(std::declval<Fun>()())>
70Ret retryAfterSignalUnlessShutdown(
71 const std::enable_if_t<true, Ret> &Fail, // Suppress deduction.
72 const Fun &F) {
73 Ret Res;
74 do {
75 if (shutdownRequested())
76 return Fail;
77 errno = 0;
78 Res = F();
79 } while (Res == Fail && errno == EINTR);
80 return Res;
81}
82
83} // namespace clangd
84} // namespace clang
85
86#endif
87

source code of clang-tools-extra/clangd/support/Shutdown.h