brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · e295576 Raw
87 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8//9// LSP specifies a protocol for shutting down: a `shutdown` request followed10// by an `exit` notification. If this protocol is followed, clangd should11// 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 flag15//  - when ClangdLSPServer gets `exit`, it returns false to indicate end-of-LSP16//  - Transport::loop() returns with no error17//  - 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 files22//23// There are a number of things that can go wrong. Some are handled here, and24// some elsewhere.25//  - `exit` notification with no `shutdown`:26//    ClangdServer::run() sees this and returns false, main() returns nonzero.27//  - stdin/stdout are closed28//    The Transport detects this while doing IO and returns an error from loop()29//    ClangdServer::run() logs a message and then returns false, etc30//  - a request thread gets stuck, so the ClangdServer destructor hangs.31//    Before returning from main(), we start a watchdog thread to abort() the32//    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 down37//    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 and40//    returns an error, etc.41//42//===----------------------------------------------------------------------===//43#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_SHUTDOWN_H44#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_SHUTDOWN_H45 46#include <cerrno>47#include <chrono>48#include <type_traits>49#include <utility>50 51namespace clang {52namespace clangd {53 54/// Causes this process to crash if still running after Timeout.55void abortAfterTimeout(std::chrono::seconds Timeout);56 57/// Sets a flag to indicate that clangd was sent a shutdown signal, and the58/// 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.61void requestShutdown();62/// Checks whether requestShutdown() was called.63/// This function is threadsafe and signal-safe.64bool shutdownRequested();65 66/// Retry an operation if it gets interrupted by a signal.67/// This is like llvm::sys::RetryAfterSignal, except that if shutdown was68/// requested (which interrupts IO), we'll fail rather than retry.69template <typename Fun, typename Ret = decltype(std::declval<Fun>()())>70Ret 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 clangd84} // namespace clang85 86#endif87