nixd
Loading...
Searching...
No Matches
Function.h
Go to the documentation of this file.
1#pragma once
2
3#include <llvm/ADT/FunctionExtras.h>
4#include <llvm/Support/Error.h>
5#include <mutex>
6#include <tuple>
7#include <utility>
8
9namespace lspserver {
10
11/// A Callback<T> is a void function that accepts Expected<T>.
12/// This is accepted by ClangdServer functions that logically return T.
13template <typename T>
14using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
15
16/// An Event<T> allows events of type T to be broadcast to listeners.
17template <typename T> class Event {
18public:
19 // A Listener is the callback through which events are delivered.
20 using Listener = std::function<void(const T &)>;
21
22 // A subscription defines the scope of when a listener should receive events.
23 // After destroying the subscription, no more events are received.
25 Event *Parent;
26 unsigned ListenerID;
27
28 Subscription(Event *Parent, unsigned ListenerID)
29 : Parent(Parent), ListenerID(ListenerID) {}
30 friend Event;
31
32 public:
33 Subscription() : Parent(nullptr) {}
34 Subscription(Subscription &&Other) : Parent(nullptr) {
35 *this = std::move(Other);
36 }
38 // If *this is active, unsubscribe.
39 if (Parent) {
40 std::lock_guard<std::recursive_mutex> Lock(Parent->ListenersMu);
41 llvm::erase_if(Parent->Listeners,
42 [&](const std::pair<Listener, unsigned> &P) {
43 return P.second == ListenerID;
44 });
45 }
46 // Take over the other subscription, and mark it inactive.
47 std::tie(Parent, ListenerID) = std::tie(Other.Parent, Other.ListenerID);
48 Other.Parent = nullptr;
49 return *this;
50 }
51 // Destroying a subscription may block if an event is being broadcast.
53 if (Parent)
54 *this = Subscription(); // Unsubscribe.
55 }
56 };
57
58 // Adds a listener that will observe all future events until the returned
59 // subscription is destroyed.
60 // May block if an event is currently being broadcast.
62 std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
63 Listeners.push_back({std::move(L), ++ListenerCount});
64 return Subscription(this, ListenerCount);
65 }
66
67 // Synchronously sends an event to all registered listeners.
68 // Must not be called from a listener to this event.
69 void broadcast(const T &V) {
70 // FIXME: it would be nice to dynamically check non-reentrancy here.
71 std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
72 for (const auto &L : Listeners)
73 L.first(V);
74 }
75
77 std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
78 assert(Listeners.empty());
79 }
80
81private:
82 static_assert(std::is_same<std::decay_t<T>, T>::value,
83 "use a plain type: event values are always passed by const&");
84
85 std::recursive_mutex ListenersMu;
86 bool IsBroadcasting = false;
87 std::vector<std::pair<Listener, unsigned>> Listeners;
88 unsigned ListenerCount = 0;
89};
90
91} // namespace lspserver
Subscription(Subscription &&Other)
Definition Function.h:34
Subscription & operator=(Subscription &&Other)
Definition Function.h:37
An Event<T> allows events of type T to be broadcast to listeners.
Definition Function.h:17
void broadcast(const T &V)
Definition Function.h:69
std::function< void(const T &)> Listener
Definition Function.h:20
Subscription observe(Listener L)
Definition Function.h:61
Whether current platform treats paths case insensitively.
Definition Connection.h:11
llvm::unique_function< void(llvm::Expected< T >)> Callback
Definition Function.h:14
bool fromJSON(const llvm::json::Value &, URIForFile &, llvm::json::Path)