1//===-- ProgressEvent.cpp ---------------------------------------*- 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#include <atomic>
10#include <chrono>
11#include <mutex>
12#include <optional>
13#include <queue>
14#include <thread>
15
16#include "DAPForward.h"
17
18#include "llvm/Support/JSON.h"
19
20namespace lldb_dap {
21
22enum ProgressEventType { progressStart, progressUpdate, progressEnd };
23
24class ProgressEvent;
25using ProgressEventReportCallback = std::function<void(ProgressEvent &)>;
26
27class ProgressEvent {
28public:
29 /// Actual constructor to use that returns an optional, as the event might be
30 /// not apt for the IDE, e.g. an unnamed start event, or a redundant one.
31 ///
32 /// \param[in] progress_id
33 /// ID for this event.
34 ///
35 /// \param[in] message
36 /// Message to display in the UI. Required for start events.
37 ///
38 /// \param[in] completed
39 /// Number of jobs completed.
40 ///
41 /// \param[in] total
42 /// Total number of jobs, or \b UINT64_MAX if not determined.
43 ///
44 /// \param[in] prev_event
45 /// Previous event if this one is an update. If \b nullptr, then a start
46 /// event will be created.
47 static std::optional<ProgressEvent>
48 Create(uint64_t progress_id, std::optional<llvm::StringRef> message,
49 uint64_t completed, uint64_t total,
50 const ProgressEvent *prev_event = nullptr);
51
52 llvm::json::Value ToJSON() const;
53
54 /// \return
55 /// \b true if two event messages would result in the same event for the
56 /// IDE, e.g. same rounded percentage.
57 bool EqualsForIDE(const ProgressEvent &other) const;
58
59 llvm::StringRef GetEventName() const;
60
61 ProgressEventType GetEventType() const;
62
63 /// Report this progress event to the provided callback only if enough time
64 /// has passed since the creation of the event and since the previous reported
65 /// update.
66 bool Report(ProgressEventReportCallback callback);
67
68 bool Reported() const;
69
70private:
71 ProgressEvent(uint64_t progress_id, std::optional<llvm::StringRef> message,
72 uint64_t completed, uint64_t total,
73 const ProgressEvent *prev_event);
74
75 uint64_t m_progress_id;
76 std::string m_message;
77 ProgressEventType m_event_type;
78 std::optional<uint32_t> m_percentage;
79 std::chrono::duration<double> m_creation_time =
80 std::chrono::system_clock::now().time_since_epoch();
81 std::chrono::duration<double> m_minimum_allowed_report_time;
82 bool m_reported = false;
83};
84
85/// Class that keeps the start event and its most recent update.
86/// It controls when the event should start being reported to the IDE.
87class ProgressEventManager {
88public:
89 ProgressEventManager(const ProgressEvent &start_event,
90 ProgressEventReportCallback report_callback);
91
92 /// Report the start event and the most recent update if the event has lasted
93 /// for long enough.
94 ///
95 /// \return
96 /// \b false if the event hasn't finished and hasn't reported anything
97 /// yet.
98 bool ReportIfNeeded();
99
100 /// Receive a new progress event for the start event and try to report it if
101 /// appropriate.
102 void Update(uint64_t progress_id, llvm::StringRef message, uint64_t completed,
103 uint64_t total);
104
105 /// \return
106 /// \b true if a \a progressEnd event has been notified. There's no
107 /// need to try to report manually an event that has finished.
108 bool Finished() const;
109
110 const ProgressEvent &GetMostRecentEvent() const;
111
112private:
113 ProgressEvent m_start_event;
114 std::optional<ProgressEvent> m_last_update_event;
115 bool m_finished;
116 ProgressEventReportCallback m_report_callback;
117};
118
119using ProgressEventManagerSP = std::shared_ptr<ProgressEventManager>;
120
121/// Class that filters out progress event messages that shouldn't be reported
122/// to the IDE, because they are invalid, they carry no new information, or they
123/// don't last long enough.
124///
125/// We need to limit the amount of events that are sent to the IDE, as they slow
126/// the render thread of the UI user, and they end up spamming the DAP
127/// connection, which also takes some processing time out of the IDE.
128class ProgressEventReporter {
129public:
130 /// \param[in] report_callback
131 /// Function to invoke to report the event to the IDE.
132 ProgressEventReporter(ProgressEventReportCallback report_callback);
133
134 ~ProgressEventReporter();
135
136 /// Add a new event to the internal queue and report the event if
137 /// appropriate.
138 void Push(uint64_t progress_id, const char *message, uint64_t completed,
139 uint64_t total);
140
141private:
142 /// Report to the IDE events that haven't been reported to the IDE and have
143 /// lasted long enough.
144 void ReportStartEvents();
145
146 ProgressEventReportCallback m_report_callback;
147 std::map<uint64_t, ProgressEventManagerSP> m_event_managers;
148 /// Queue of start events in chronological order
149 std::queue<ProgressEventManagerSP> m_unreported_start_events;
150 /// Thread used to invoke \a ReportStartEvents periodically.
151 std::thread m_thread;
152 std::atomic<bool> m_thread_should_exit;
153 /// Mutex that prevents running \a Push and \a ReportStartEvents
154 /// simultaneously, as both read and modify the same underlying objects.
155 std::mutex m_mutex;
156};
157
158} // namespace lldb_dap
159

Provided by KDAB

Privacy Policy
Update your C++ knowledge – Modern C++11/14/17 Training
Find out more

source code of lldb/tools/lldb-dap/ProgressEvent.h