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