Mercurial > hg > CbC > CbC_llvm
diff clang-tools-extra/clangd/support/Function.h @ 173:0572611fdcc8 llvm10 llvm12
reorgnization done
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 25 May 2020 11:55:54 +0900 |
parents | |
children | 2e18cbf3894f |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/clang-tools-extra/clangd/support/Function.h Mon May 25 11:55:54 2020 +0900 @@ -0,0 +1,108 @@ +//===--- 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_SUPPORT_FUNCTION_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_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