//===--- Function.h - Utility callable wrappers -----------------*- C++-*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file provides utilities for callable objects. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FUNCTION_H #include "llvm/ADT/FunctionExtras.h" #include "llvm/Support/Error.h" #include #include #include namespace clang { namespace clangd { /// A Callback is a void function that accepts Expected. /// This is accepted by ClangdServer functions that logically return T. template using Callback = llvm::unique_function)>; /// Stores a callable object (Func) and arguments (Args) and allows to call the /// callable with provided arguments later using `operator ()`. The arguments /// are std::forward'ed into the callable in the body of `operator()`. Therefore /// `operator()` can only be called once, as some of the arguments could be /// std::move'ed into the callable on first call. template struct ForwardBinder { using Tuple = std::tuple::type, typename std::decay::type...>; Tuple FuncWithArguments; #ifndef NDEBUG bool WasCalled = false; #endif public: ForwardBinder(Tuple FuncWithArguments) : FuncWithArguments(std::move(FuncWithArguments)) {} private: template auto CallImpl(llvm::integer_sequence Seq, RestArgs &&... Rest) -> decltype(std::get<0>(this->FuncWithArguments)( std::forward(std::get(this->FuncWithArguments))..., std::forward(Rest)...)) { return std::get<0>(this->FuncWithArguments)( std::forward(std::get(this->FuncWithArguments))..., std::forward(Rest)...); } public: template auto operator()(RestArgs &&... Rest) -> decltype(this->CallImpl(llvm::index_sequence_for(), std::forward(Rest)...)) { #ifndef NDEBUG assert(!WasCalled && "Can only call result of Bind once."); WasCalled = true; #endif return CallImpl(llvm::index_sequence_for(), std::forward(Rest)...); } }; /// Creates an object that stores a callable (\p F) and first arguments to the /// callable (\p As) and allows to call \p F with \Args at a later point. /// Similar to std::bind, but also works with move-only \p F and \p As. /// /// The returned object must be called no more than once, as \p As are /// std::forwarded'ed (therefore can be moved) into \p F during the call. template ForwardBinder Bind(Func F, Args &&... As) { return ForwardBinder( std::make_tuple(std::forward(F), std::forward(As)...)); } /// An Event allows events of type T to be broadcast to listeners. template class Event { public: // A Listener is the callback through which events are delivered. using Listener = std::function; // A subscription defines the scope of when a listener should receive events. // After destroying the subscription, no more events are received. class LLVM_NODISCARD Subscription { Event *Parent; unsigned ListenerID; Subscription(Event *Parent, unsigned ListenerID) : Parent(Parent), ListenerID(ListenerID) {} friend Event; public: Subscription() : Parent(nullptr) {} Subscription(Subscription &&Other) : Parent(nullptr) { *this = std::move(Other); } Subscription &operator=(Subscription &&Other) { // If *this is active, unsubscribe. if (Parent) { std::lock_guard(Parent->ListenersMu); llvm::erase_if(Parent->Listeners, [&](const std::pair &P) { return P.second == ListenerID; }); } // Take over the other subscription, and mark it inactive. std::tie(Parent, ListenerID) = std::tie(Other.Parent, Other.ListenerID); Other.Parent = nullptr; return *this; } // Destroying a subscription may block if an event is being broadcast. ~Subscription() { if (Parent) *this = Subscription(); // Unsubscribe. } }; // Adds a listener that will observe all future events until the returned // subscription is destroyed. // May block if an event is currently being broadcast. Subscription observe(Listener L) { std::lock_guard Lock(ListenersMu); Listeners.push_back({std::move(L), ++ListenerCount}); return Subscription(this, ListenerCount); } // Synchronously sends an event to all registered listeners. // Must not be called from a listener to this event. void broadcast(const T &V) { // FIXME: it would be nice to dynamically check non-reentrancy here. std::lock_guard Lock(ListenersMu); for (const auto &L : Listeners) L.first(V); } ~Event() { std::lock_guard Lock(ListenersMu); assert(Listeners.empty()); } private: static_assert(std::is_same::type, T>::value, "use a plain type: event values are always passed by const&"); std::recursive_mutex ListenersMu; bool IsBroadcasting = false; std::vector> Listeners; unsigned ListenerCount = 0; }; } // namespace clangd } // namespace clang #endif