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 |
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 [[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> Lock(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<std::decay_t<T>, T>::value, |
97 | "use a plain type: event values are always passed by const&" ); |
98 | |
99 | std::recursive_mutex ListenersMu; |
100 | std::vector<std::pair<Listener, unsigned>> Listeners; |
101 | unsigned ListenerCount = 0; |
102 | }; |
103 | |
104 | } // namespace clangd |
105 | } // namespace clang |
106 | |
107 | #endif |
108 | |