forked from OSchip/llvm-project
109 lines
3.6 KiB
C++
109 lines
3.6 KiB
C++
//===--- Function.h - Utility callable wrappers -----------------*- C++-*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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>)>;
|
|
|
|
/// 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
|