1 //===--- Shutdown.h - Unclean exit scenarios --------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
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
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
48 #include <type_traits>
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.
75 if (shutdownRequested())
79 } while (Res
== Fail
&& errno
== EINTR
);