llvm-project/clang-tools-extra/clangd/Function.h

166 lines
5.8 KiB
C++

//===--- 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 <mutex>
#include <tuple>
#include <utility>
namespace clang {
namespace clangd {
/// A Callback<T> is a void function that accepts Expected<T>.
/// This is accepted by ClangdServer functions that logically return T.
template <typename T>
using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
/// 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 <class Func, class... Args> struct ForwardBinder {
using Tuple = std::tuple<typename std::decay<Func>::type,
typename std::decay<Args>::type...>;
Tuple FuncWithArguments;
#ifndef NDEBUG
bool WasCalled = false;
#endif
public:
ForwardBinder(Tuple FuncWithArguments)
: FuncWithArguments(std::move(FuncWithArguments)) {}
private:
template <std::size_t... Indexes, class... RestArgs>
auto CallImpl(llvm::integer_sequence<std::size_t, Indexes...> Seq,
RestArgs &&... Rest)
-> decltype(std::get<0>(this->FuncWithArguments)(
std::forward<Args>(std::get<Indexes + 1>(this->FuncWithArguments))...,
std::forward<RestArgs>(Rest)...)) {
return std::get<0>(this->FuncWithArguments)(
std::forward<Args>(std::get<Indexes + 1>(this->FuncWithArguments))...,
std::forward<RestArgs>(Rest)...);
}
public:
template <class... RestArgs>
auto operator()(RestArgs &&... Rest)
-> decltype(this->CallImpl(llvm::index_sequence_for<Args...>(),
std::forward<RestArgs>(Rest)...)) {
#ifndef NDEBUG
assert(!WasCalled && "Can only call result of Bind once.");
WasCalled = true;
#endif
return CallImpl(llvm::index_sequence_for<Args...>(),
std::forward<RestArgs>(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 <class Func, class... Args>
ForwardBinder<Func, Args...> Bind(Func F, Args &&... As) {
return ForwardBinder<Func, Args...>(
std::make_tuple(std::forward<Func>(F), std::forward<Args>(As)...));
}
/// An Event<T> allows events of type T to be broadcast to listeners.
template <typename T> class Event {
public:
// A Listener is the callback through which events are delivered.
using Listener = std::function<void(const T &)>;
// 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<std::recursive_mutex>(Parent->ListenersMu);
llvm::erase_if(Parent->Listeners,
[&](const std::pair<Listener, unsigned> &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<std::recursive_mutex> 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<std::recursive_mutex> Lock(ListenersMu);
for (const auto &L : Listeners)
L.first(V);
}
~Event() {
std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
assert(Listeners.empty());
}
private:
static_assert(std::is_same<typename std::decay<T>::type, T>::value,
"use a plain type: event values are always passed by const&");
std::recursive_mutex ListenersMu;
bool IsBroadcasting = false;
std::vector<std::pair<Listener, unsigned>> Listeners;
unsigned ListenerCount = 0;
};
} // namespace clangd
} // namespace clang
#endif