• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  
49  namespace clang {
50  namespace clangd {
51  
52  /// Causes this process to crash if still running after Timeout.
53  void abortAfterTimeout(std::chrono::seconds Timeout);
54  
55  /// Sets a flag to indicate that clangd was sent a shutdown signal, and the
56  /// transport loop should exit at the next opportunity.
57  /// If shutdown was already requested, aborts the process.
58  /// This function is threadsafe and signal-safe.
59  void requestShutdown();
60  /// Checks whether requestShutdown() was called.
61  /// This function is threadsafe and signal-safe.
62  bool shutdownRequested();
63  
64  /// Retry an operation if it gets interrupted by a signal.
65  /// This is like llvm::sys::RetryAfterSignal, except that if shutdown was
66  /// requested (which interrupts IO), we'll fail rather than retry.
67  template <typename Fun, typename Ret = decltype(std::declval<Fun>()())>
retryAfterSignalUnlessShutdown(const std::enable_if_t<true,Ret> & Fail,const Fun & F)68  Ret retryAfterSignalUnlessShutdown(
69      const std::enable_if_t<true, Ret> &Fail, // Suppress deduction.
70      const Fun &F) {
71    Ret Res;
72    do {
73      if (shutdownRequested())
74        return Fail;
75      errno = 0;
76      Res = F();
77    } while (Res == Fail && errno == EINTR);
78    return Res;
79  }
80  
81  } // namespace clangd
82  } // namespace clang
83  
84  #endif
85