1 //===--- Function.h - Utility callable wrappers -----------------*- C++-*-===//
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
7 //===----------------------------------------------------------------------===//
9 // This file provides utilities for callable objects.
11 //===----------------------------------------------------------------------===//
13 #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H
14 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H
16 #include "llvm/ADT/FunctionExtras.h"
17 #include "llvm/Support/Error.h"
25 /// A Callback<T> is a void function that accepts Expected<T>.
26 /// This is accepted by ClangdServer functions that logically return T.
28 using Callback
= llvm::unique_function
<void(llvm::Expected
<T
>)>;
30 /// An Event<T> allows events of type T to be broadcast to listeners.
31 template <typename T
> class Event
{
33 // A Listener is the callback through which events are delivered.
34 using Listener
= std::function
<void(const T
&)>;
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
{
42 Subscription(Event
*Parent
, unsigned ListenerID
)
43 : Parent(Parent
), ListenerID(ListenerID
) {}
47 Subscription() : Parent(nullptr) {}
48 Subscription(Subscription
&&Other
) : Parent(nullptr) {
49 *this = std::move(Other
);
51 Subscription
&operator=(Subscription
&&Other
) {
52 // If *this is active, unsubscribe.
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
;
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;
65 // Destroying a subscription may block if an event is being broadcast.
68 *this = Subscription(); // Unsubscribe.
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
);
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
)
91 std::lock_guard
<std::recursive_mutex
> Lock(ListenersMu
);
92 assert(Listeners
.empty());
96 static_assert(std::is_same
<std::decay_t
<T
>, T
>::value
,
97 "use a plain type: event values are always passed by const&");
99 std::recursive_mutex ListenersMu
;
100 std::vector
<std::pair
<Listener
, unsigned>> Listeners
;
101 unsigned ListenerCount
= 0;
104 } // namespace clangd