[Reland][Runtimes] Merge 'compile_commands.json' files from runtimes build (#116303)
[llvm-project.git] / clang-tools-extra / clangd / support / Shutdown.h
blobe295576c1fda00704c4d45e0b1eede9f47ec4b94
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.
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
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.
42 //===----------------------------------------------------------------------===//
43 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_SHUTDOWN_H
44 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_SHUTDOWN_H
46 #include <cerrno>
47 #include <chrono>
48 #include <type_traits>
49 #include <utility>
51 namespace clang {
52 namespace clangd {
54 /// Causes this process to crash if still running after Timeout.
55 void abortAfterTimeout(std::chrono::seconds Timeout);
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();
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;
83 } // namespace clangd
84 } // namespace clang
86 #endif