diff options
Diffstat (limited to 'clang-tools-extra/clangd/Function.h')
| -rw-r--r-- | clang-tools-extra/clangd/Function.h | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/clang-tools-extra/clangd/Function.h b/clang-tools-extra/clangd/Function.h index 88a5bc00025..52effe0d9cc 100644 --- a/clang-tools-extra/clangd/Function.h +++ b/clang-tools-extra/clangd/Function.h @@ -16,6 +16,7 @@ #include "llvm/ADT/FunctionExtras.h" #include "llvm/Support/Error.h" +#include <mutex> #include <tuple> #include <utility> @@ -83,6 +84,82 @@ ForwardBinder<Func, Args...> Bind(Func F, Args &&... As) { 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 |

