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