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
22namespace clang {
23namespace 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.
27template <typename T>
28using Callback = llvm::unique_function<void(llvm::Expected<T>)>;
29
30/// An Event<T> allows events of type T to be broadcast to listeners.
31template <typename T> class Event {
32public:
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
95private:
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

source code of clang-tools-extra/clangd/support/Function.h