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 |
45 | |
46 | #include <cerrno> |
47 | #include <chrono> |
48 | #include <type_traits> |
49 | #include <utility> |
50 | |
51 | namespace clang { |
52 | namespace clangd { |
53 | |
54 | /// Causes this process to crash if still running after Timeout. |
55 | void 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. |
61 | void requestShutdown(); |
62 | /// Checks whether requestShutdown() was called. |
63 | /// This function is threadsafe and signal-safe. |
64 | bool 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. |
69 | template <typename Fun, typename Ret = decltype(std::declval<Fun>()())> |
70 | Ret 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 | |