173
|
1 //===--- Function.h - Utility callable wrappers -----------------*- C++-*-===//
|
|
2 //
|
|
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
4 // See https://llvm.org/LICENSE.txt for license information.
|
|
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
6 //
|
|
7 //===----------------------------------------------------------------------===//
|
|
8 //
|
|
9 // This file provides utilities for callable objects.
|
|
10 //
|
|
11 //===----------------------------------------------------------------------===//
|
|
12
|
|
13 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H
|
|
14 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H
|
|
15
|
|
16 #include "llvm/ADT/FunctionExtras.h"
|
|
17 #include "llvm/Support/Error.h"
|
|
18 #include <mutex>
|
|
19 #include <tuple>
|
|
20 #include <utility>
|
|
21
|
|
22 namespace clang {
|
|
23 namespace clangd {
|
|
24
|
|
25 /// A Callback<T> is a void function that accepts Expected<T>.
|
|
26 /// This is accepted by ClangdServer functions that logically return T.
|
|
27 template <typename T>
|
|
28 using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
|
|
29
|
|
30 /// An Event<T> allows events of type T to be broadcast to listeners.
|
|
31 template <typename T> class Event {
|
|
32 public:
|
|
33 // A Listener is the callback through which events are delivered.
|
|
34 using Listener = std::function<void(const T &)>;
|
|
35
|
|
36 // A subscription defines the scope of when a listener should receive events.
|
|
37 // After destroying the subscription, no more events are received.
|
|
38 class LLVM_NODISCARD Subscription {
|
|
39 Event *Parent;
|
|
40 unsigned ListenerID;
|
|
41
|
|
42 Subscription(Event *Parent, unsigned ListenerID)
|
|
43 : Parent(Parent), ListenerID(ListenerID) {}
|
|
44 friend Event;
|
|
45
|
|
46 public:
|
|
47 Subscription() : Parent(nullptr) {}
|
|
48 Subscription(Subscription &&Other) : Parent(nullptr) {
|
|
49 *this = std::move(Other);
|
|
50 }
|
|
51 Subscription &operator=(Subscription &&Other) {
|
|
52 // If *this is active, unsubscribe.
|
|
53 if (Parent) {
|
|
54 std::lock_guard<std::recursive_mutex>(Parent->ListenersMu);
|
|
55 llvm::erase_if(Parent->Listeners,
|
|
56 [&](const std::pair<Listener, unsigned> &P) {
|
|
57 return P.second == ListenerID;
|
|
58 });
|
|
59 }
|
|
60 // Take over the other subscription, and mark it inactive.
|
|
61 std::tie(Parent, ListenerID) = std::tie(Other.Parent, Other.ListenerID);
|
|
62 Other.Parent = nullptr;
|
|
63 return *this;
|
|
64 }
|
|
65 // Destroying a subscription may block if an event is being broadcast.
|
|
66 ~Subscription() {
|
|
67 if (Parent)
|
|
68 *this = Subscription(); // Unsubscribe.
|
|
69 }
|
|
70 };
|
|
71
|
|
72 // Adds a listener that will observe all future events until the returned
|
|
73 // subscription is destroyed.
|
|
74 // May block if an event is currently being broadcast.
|
|
75 Subscription observe(Listener L) {
|
|
76 std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
|
|
77 Listeners.push_back({std::move(L), ++ListenerCount});
|
|
78 return Subscription(this, ListenerCount);
|
|
79 }
|
|
80
|
|
81 // Synchronously sends an event to all registered listeners.
|
|
82 // Must not be called from a listener to this event.
|
|
83 void broadcast(const T &V) {
|
|
84 // FIXME: it would be nice to dynamically check non-reentrancy here.
|
|
85 std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
|
|
86 for (const auto &L : Listeners)
|
|
87 L.first(V);
|
|
88 }
|
|
89
|
|
90 ~Event() {
|
|
91 std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
|
|
92 assert(Listeners.empty());
|
|
93 }
|
|
94
|
|
95 private:
|
|
96 static_assert(std::is_same<typename std::decay<T>::type, T>::value,
|
|
97 "use a plain type: event values are always passed by const&");
|
|
98
|
|
99 std::recursive_mutex ListenersMu;
|
|
100 bool IsBroadcasting = false;
|
|
101 std::vector<std::pair<Listener, unsigned>> Listeners;
|
|
102 unsigned ListenerCount = 0;
|
|
103 };
|
|
104
|
|
105 } // namespace clangd
|
|
106 } // namespace clang
|
|
107
|
|
108 #endif
|