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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This file provides utilities for callable objects. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H 14 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H 15 16 #include "llvm/ADT/FunctionExtras.h" 17 #include "llvm/Support/Error.h" 18 #include <mutex> 19 #include <tuple> 20 #include <utility> 21 22 namespace clang { 23 namespace 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. 27 template <typename T> 28 using Callback = llvm::unique_function<void(llvm::Expected<T>)>; 29 30 /// An Event<T> allows events of type T to be broadcast to listeners. 31 template <typename T> class Event { 32 public: 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 LLVM_NODISCARD Subscription { 39 Event *Parent; 40 unsigned ListenerID; 41 Subscription(Event * Parent,unsigned ListenerID)42 Subscription(Event *Parent, unsigned ListenerID) 43 : Parent(Parent), ListenerID(ListenerID) {} 44 friend Event; 45 46 public: Subscription()47 Subscription() : Parent(nullptr) {} Subscription(Subscription && Other)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>(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. ~Subscription()66 ~Subscription() { 67 if (Parent) 68 *this = Subscription(); // Unsubscribe. 69 } 70 }; 71 72 // Adds a listener that will observe all future events until the returned 73 // subscription is destroyed. 74 // May block if an event is currently being broadcast. observe(Listener L)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. broadcast(const T & V)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 ~Event()90 ~Event() { 91 std::lock_guard<std::recursive_mutex> Lock(ListenersMu); 92 assert(Listeners.empty()); 93 } 94 95 private: 96 static_assert(std::is_same<typename std::decay<T>::type, T>::value, 97 "use a plain type: event values are always passed by const&"); 98 99 std::recursive_mutex ListenersMu; 100 bool IsBroadcasting = false; 101 std::vector<std::pair<Listener, unsigned>> Listeners; 102 unsigned ListenerCount = 0; 103 }; 104 105 } // namespace clangd 106 } // namespace clang 107 108 #endif 109