File: Function.h

package info (click to toggle)
llvm-toolchain-19 1%3A19.1.7-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,998,520 kB
  • sloc: cpp: 6,951,680; ansic: 1,486,157; asm: 913,598; python: 232,024; f90: 80,126; objc: 75,281; lisp: 37,276; pascal: 16,990; sh: 10,009; ml: 5,058; perl: 4,724; awk: 3,523; makefile: 3,167; javascript: 2,504; xml: 892; fortran: 664; cs: 573
file content (107 lines) | stat: -rw-r--r-- 3,657 bytes parent folder | download | duplicates (10)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
//===--- Function.h - Utility callable wrappers  -----------------*- C++-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides utilities for callable objects.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H
#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SUPPORT_FUNCTION_H

#include "llvm/ADT/FunctionExtras.h"
#include "llvm/Support/Error.h"
#include <mutex>
#include <tuple>
#include <utility>

namespace clang {
namespace clangd {

/// A Callback<T> is a void function that accepts Expected<T>.
/// This is accepted by ClangdServer functions that logically return T.
template <typename T>
using Callback = llvm::unique_function<void(llvm::Expected<T>)>;

/// An Event<T> allows events of type T to be broadcast to listeners.
template <typename T> class Event {
public:
  // A Listener is the callback through which events are delivered.
  using Listener = std::function<void(const T &)>;

  // A subscription defines the scope of when a listener should receive events.
  // After destroying the subscription, no more events are received.
  class [[nodiscard]] Subscription {
    Event *Parent;
    unsigned ListenerID;

    Subscription(Event *Parent, unsigned ListenerID)
        : Parent(Parent), ListenerID(ListenerID) {}
    friend Event;

  public:
    Subscription() : Parent(nullptr) {}
    Subscription(Subscription &&Other) : Parent(nullptr) {
      *this = std::move(Other);
    }
    Subscription &operator=(Subscription &&Other) {
      // If *this is active, unsubscribe.
      if (Parent) {
        std::lock_guard<std::recursive_mutex> Lock(Parent->ListenersMu);
        llvm::erase_if(Parent->Listeners,
                       [&](const std::pair<Listener, unsigned> &P) {
                         return P.second == ListenerID;
                       });
      }
      // Take over the other subscription, and mark it inactive.
      std::tie(Parent, ListenerID) = std::tie(Other.Parent, Other.ListenerID);
      Other.Parent = nullptr;
      return *this;
    }
    // Destroying a subscription may block if an event is being broadcast.
    ~Subscription() {
      if (Parent)
        *this = Subscription(); // Unsubscribe.
    }
  };

  // Adds a listener that will observe all future events until the returned
  // subscription is destroyed.
  // May block if an event is currently being broadcast.
  Subscription observe(Listener L) {
    std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
    Listeners.push_back({std::move(L), ++ListenerCount});
    return Subscription(this, ListenerCount);
  }

  // Synchronously sends an event to all registered listeners.
  // Must not be called from a listener to this event.
  void broadcast(const T &V) {
    // FIXME: it would be nice to dynamically check non-reentrancy here.
    std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
    for (const auto &L : Listeners)
      L.first(V);
  }

  ~Event() {
    std::lock_guard<std::recursive_mutex> Lock(ListenersMu);
    assert(Listeners.empty());
  }

private:
  static_assert(std::is_same<std::decay_t<T>, T>::value,
                "use a plain type: event values are always passed by const&");

  std::recursive_mutex ListenersMu;
  std::vector<std::pair<Listener, unsigned>> Listeners;
  unsigned ListenerCount = 0;
};

} // namespace clangd
} // namespace clang

#endif