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