brintos

brintos / llvm-project-archived public Read only

0
0
Text · 3.6 KiB · a0bce83 Raw
108 lines · c
1//===--- Function.h - Utility callable wrappers  -----------------*- 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// This file provides utilities for callable objects.10//11//===----------------------------------------------------------------------===//12 13#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H14#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H15 16#include "llvm/ADT/FunctionExtras.h"17#include "llvm/Support/Error.h"18#include <mutex>19#include <tuple>20#include <utility>21 22namespace clang {23namespace clangd {24 25/// A Callback<T> is a void function that accepts Expected<T>.26/// This is accepted by ClangdServer functions that logically return T.27template <typename T>28using Callback = llvm::unique_function<void(llvm::Expected<T>)>;29 30/// An Event<T> allows events of type T to be broadcast to listeners.31template <typename T> class Event {32public:33  // A Listener is the callback through which events are delivered.34  using Listener = std::function<void(const T &)>;35 36  // A subscription defines the scope of when a listener should receive events.37  // After destroying the subscription, no more events are received.38  class [[nodiscard]] Subscription {39    Event *Parent;40    unsigned ListenerID;41 42    Subscription(Event *Parent, unsigned ListenerID)43        : Parent(Parent), ListenerID(ListenerID) {}44    friend Event;45 46  public:47    Subscription() : Parent(nullptr) {}48    Subscription(Subscription &&Other) : Parent(nullptr) {49      *this = std::move(Other);50    }51    Subscription &operator=(Subscription &&Other) {52      // If *this is active, unsubscribe.53      if (Parent) {54        std::lock_guard<std::recursive_mutex> Lock(Parent->ListenersMu);55        llvm::erase_if(Parent->Listeners,56                       [&](const std::pair<Listener, unsigned> &P) {57                         return P.second == ListenerID;58                       });59      }60      // Take over the other subscription, and mark it inactive.61      std::tie(Parent, ListenerID) = std::tie(Other.Parent, Other.ListenerID);62      Other.Parent = nullptr;63      return *this;64    }65    // Destroying a subscription may block if an event is being broadcast.66    ~Subscription() {67      if (Parent)68        *this = Subscription(); // Unsubscribe.69    }70  };71 72  // Adds a listener that will observe all future events until the returned73  // subscription is destroyed.74  // May block if an event is currently being broadcast.75  Subscription observe(Listener L) {76    std::lock_guard<std::recursive_mutex> Lock(ListenersMu);77    Listeners.push_back({std::move(L), ++ListenerCount});78    return Subscription(this, ListenerCount);79  }80 81  // Synchronously sends an event to all registered listeners.82  // Must not be called from a listener to this event.83  void broadcast(const T &V) {84    // FIXME: it would be nice to dynamically check non-reentrancy here.85    std::lock_guard<std::recursive_mutex> Lock(ListenersMu);86    for (const auto &L : Listeners)87      L.first(V);88  }89 90  ~Event() {91    std::lock_guard<std::recursive_mutex> Lock(ListenersMu);92    assert(Listeners.empty());93  }94 95private:96  static_assert(std::is_same<std::decay_t<T>, T>::value,97                "use a plain type: event values are always passed by const&");98 99  std::recursive_mutex ListenersMu;100  std::vector<std::pair<Listener, unsigned>> Listeners;101  unsigned ListenerCount = 0;102};103 104} // namespace clangd105} // namespace clang106 107#endif108