1cc88d301SWalter Erquinigo //===-- ProgressEvent.cpp ---------------------------------------*- C++ -*-===//
2cc88d301SWalter Erquinigo //
3cc88d301SWalter Erquinigo // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4cc88d301SWalter Erquinigo // See https://llvm.org/LICENSE.txt for license information.
5cc88d301SWalter Erquinigo // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6cc88d301SWalter Erquinigo //
7cc88d301SWalter Erquinigo //===----------------------------------------------------------------------===//
8cc88d301SWalter Erquinigo
9cc88d301SWalter Erquinigo #include "ProgressEvent.h"
10cc88d301SWalter Erquinigo
11cc88d301SWalter Erquinigo #include "JSONUtils.h"
12cc88d301SWalter Erquinigo
13cc88d301SWalter Erquinigo using namespace lldb_vscode;
14cc88d301SWalter Erquinigo using namespace llvm;
15cc88d301SWalter Erquinigo
16bff2b9adSWalter Erquinigo // The minimum duration of an event for it to be reported
17bff2b9adSWalter Erquinigo const std::chrono::duration<double> kStartProgressEventReportDelay =
18bff2b9adSWalter Erquinigo std::chrono::seconds(1);
19bff2b9adSWalter Erquinigo // The minimum time interval between update events for reporting. If multiple
20bff2b9adSWalter Erquinigo // updates fall within the same time interval, only the latest is reported.
21bff2b9adSWalter Erquinigo const std::chrono::duration<double> kUpdateProgressEventReportDelay =
22bff2b9adSWalter Erquinigo std::chrono::milliseconds(250);
23f84615a5SWalter Erquinigo
ProgressEvent(uint64_t progress_id,Optional<StringRef> message,uint64_t completed,uint64_t total,const ProgressEvent * prev_event)24bff2b9adSWalter Erquinigo ProgressEvent::ProgressEvent(uint64_t progress_id, Optional<StringRef> message,
25bff2b9adSWalter Erquinigo uint64_t completed, uint64_t total,
26bff2b9adSWalter Erquinigo const ProgressEvent *prev_event)
27bff2b9adSWalter Erquinigo : m_progress_id(progress_id) {
28bff2b9adSWalter Erquinigo if (message)
29bff2b9adSWalter Erquinigo m_message = message->str();
30bff2b9adSWalter Erquinigo
31bff2b9adSWalter Erquinigo const bool calculate_percentage = total != UINT64_MAX;
32bff2b9adSWalter Erquinigo if (completed == 0) {
33bff2b9adSWalter Erquinigo // Start event
34bff2b9adSWalter Erquinigo m_event_type = progressStart;
35bff2b9adSWalter Erquinigo // Wait a bit before reporting the start event in case in completes really
36bff2b9adSWalter Erquinigo // quickly.
37bff2b9adSWalter Erquinigo m_minimum_allowed_report_time =
38bff2b9adSWalter Erquinigo m_creation_time + kStartProgressEventReportDelay;
39bff2b9adSWalter Erquinigo if (calculate_percentage)
40bff2b9adSWalter Erquinigo m_percentage = 0;
41bff2b9adSWalter Erquinigo } else if (completed == total) {
42bff2b9adSWalter Erquinigo // End event
43bff2b9adSWalter Erquinigo m_event_type = progressEnd;
44bff2b9adSWalter Erquinigo // We should report the end event right away.
45bff2b9adSWalter Erquinigo m_minimum_allowed_report_time = std::chrono::seconds::zero();
46bff2b9adSWalter Erquinigo if (calculate_percentage)
47bff2b9adSWalter Erquinigo m_percentage = 100;
48bff2b9adSWalter Erquinigo } else {
49bff2b9adSWalter Erquinigo // Update event
50*8ea1a630SWalter Erquinigo m_event_type = progressUpdate;
51bff2b9adSWalter Erquinigo m_percentage = std::min(
52bff2b9adSWalter Erquinigo (uint32_t)((double)completed / (double)total * 100.0), (uint32_t)99);
53bff2b9adSWalter Erquinigo if (prev_event->Reported()) {
54bff2b9adSWalter Erquinigo // Add a small delay between reports
55bff2b9adSWalter Erquinigo m_minimum_allowed_report_time =
56bff2b9adSWalter Erquinigo prev_event->m_minimum_allowed_report_time +
57bff2b9adSWalter Erquinigo kUpdateProgressEventReportDelay;
58bff2b9adSWalter Erquinigo } else {
59bff2b9adSWalter Erquinigo // We should use the previous timestamp, as it's still pending
60bff2b9adSWalter Erquinigo m_minimum_allowed_report_time = prev_event->m_minimum_allowed_report_time;
61bff2b9adSWalter Erquinigo }
62bff2b9adSWalter Erquinigo }
63cc88d301SWalter Erquinigo }
64cc88d301SWalter Erquinigo
Create(uint64_t progress_id,Optional<StringRef> message,uint64_t completed,uint64_t total,const ProgressEvent * prev_event)65bff2b9adSWalter Erquinigo Optional<ProgressEvent> ProgressEvent::Create(uint64_t progress_id,
66bff2b9adSWalter Erquinigo Optional<StringRef> message,
67bff2b9adSWalter Erquinigo uint64_t completed,
68bff2b9adSWalter Erquinigo uint64_t total,
69bff2b9adSWalter Erquinigo const ProgressEvent *prev_event) {
70bff2b9adSWalter Erquinigo // If it's an update without a previous event, we abort
71bff2b9adSWalter Erquinigo if (completed > 0 && completed < total && !prev_event)
72bff2b9adSWalter Erquinigo return None;
73bff2b9adSWalter Erquinigo ProgressEvent event(progress_id, message, completed, total, prev_event);
74bff2b9adSWalter Erquinigo // We shouldn't show unnamed start events in the IDE
75bff2b9adSWalter Erquinigo if (event.GetEventType() == progressStart && event.GetEventName().empty())
76bff2b9adSWalter Erquinigo return None;
77bff2b9adSWalter Erquinigo
78bff2b9adSWalter Erquinigo if (prev_event && prev_event->EqualsForIDE(event))
79bff2b9adSWalter Erquinigo return None;
80bff2b9adSWalter Erquinigo
81bff2b9adSWalter Erquinigo return event;
82bff2b9adSWalter Erquinigo }
83bff2b9adSWalter Erquinigo
EqualsForIDE(const ProgressEvent & other) const84bff2b9adSWalter Erquinigo bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {
85cc88d301SWalter Erquinigo return m_progress_id == other.m_progress_id &&
86cc88d301SWalter Erquinigo m_event_type == other.m_event_type &&
87cc88d301SWalter Erquinigo m_percentage == other.m_percentage;
88cc88d301SWalter Erquinigo }
89cc88d301SWalter Erquinigo
GetEventType() const90bff2b9adSWalter Erquinigo ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }
91bff2b9adSWalter Erquinigo
GetEventName() const92bff2b9adSWalter Erquinigo StringRef ProgressEvent::GetEventName() const {
93cc88d301SWalter Erquinigo if (m_event_type == progressStart)
94cc88d301SWalter Erquinigo return "progressStart";
95cc88d301SWalter Erquinigo else if (m_event_type == progressEnd)
96cc88d301SWalter Erquinigo return "progressEnd";
97f84615a5SWalter Erquinigo else
98bff2b9adSWalter Erquinigo return "progressUpdate";
99cc88d301SWalter Erquinigo }
100cc88d301SWalter Erquinigo
ToJSON() const101cc88d301SWalter Erquinigo json::Value ProgressEvent::ToJSON() const {
102cc88d301SWalter Erquinigo llvm::json::Object event(CreateEventObject(GetEventName()));
103cc88d301SWalter Erquinigo llvm::json::Object body;
104cc88d301SWalter Erquinigo
105cc88d301SWalter Erquinigo std::string progress_id_str;
106cc88d301SWalter Erquinigo llvm::raw_string_ostream progress_id_strm(progress_id_str);
107cc88d301SWalter Erquinigo progress_id_strm << m_progress_id;
108cc88d301SWalter Erquinigo progress_id_strm.flush();
109cc88d301SWalter Erquinigo body.try_emplace("progressId", progress_id_str);
110cc88d301SWalter Erquinigo
111cc88d301SWalter Erquinigo if (m_event_type == progressStart) {
112cc88d301SWalter Erquinigo EmplaceSafeString(body, "title", m_message);
113cc88d301SWalter Erquinigo body.try_emplace("cancellable", false);
114cc88d301SWalter Erquinigo }
115cc88d301SWalter Erquinigo
116bff2b9adSWalter Erquinigo std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));
117cc88d301SWalter Erquinigo EmplaceSafeString(body, "timestamp", timestamp);
118cc88d301SWalter Erquinigo
119cc88d301SWalter Erquinigo if (m_percentage)
120cc88d301SWalter Erquinigo body.try_emplace("percentage", *m_percentage);
121cc88d301SWalter Erquinigo
122cc88d301SWalter Erquinigo event.try_emplace("body", std::move(body));
123cc88d301SWalter Erquinigo return json::Value(std::move(event));
124cc88d301SWalter Erquinigo }
125cc88d301SWalter Erquinigo
Report(ProgressEventReportCallback callback)126bff2b9adSWalter Erquinigo bool ProgressEvent::Report(ProgressEventReportCallback callback) {
127bff2b9adSWalter Erquinigo if (Reported())
128bff2b9adSWalter Erquinigo return true;
129bff2b9adSWalter Erquinigo if (std::chrono::system_clock::now().time_since_epoch() <
130bff2b9adSWalter Erquinigo m_minimum_allowed_report_time)
131bff2b9adSWalter Erquinigo return false;
132cc88d301SWalter Erquinigo
133bff2b9adSWalter Erquinigo m_reported = true;
134bff2b9adSWalter Erquinigo callback(*this);
135bff2b9adSWalter Erquinigo return true;
136bff2b9adSWalter Erquinigo }
137cc88d301SWalter Erquinigo
Reported() const138bff2b9adSWalter Erquinigo bool ProgressEvent::Reported() const { return m_reported; }
139bff2b9adSWalter Erquinigo
ProgressEventManager(const ProgressEvent & start_event,ProgressEventReportCallback report_callback)140bff2b9adSWalter Erquinigo ProgressEventManager::ProgressEventManager(
141bff2b9adSWalter Erquinigo const ProgressEvent &start_event,
142bff2b9adSWalter Erquinigo ProgressEventReportCallback report_callback)
143bff2b9adSWalter Erquinigo : m_start_event(start_event), m_finished(false),
144bff2b9adSWalter Erquinigo m_report_callback(report_callback) {}
145bff2b9adSWalter Erquinigo
ReportIfNeeded()146bff2b9adSWalter Erquinigo bool ProgressEventManager::ReportIfNeeded() {
147bff2b9adSWalter Erquinigo // The event finished before we were able to report it.
148bff2b9adSWalter Erquinigo if (!m_start_event.Reported() && Finished())
149bff2b9adSWalter Erquinigo return true;
150bff2b9adSWalter Erquinigo
151bff2b9adSWalter Erquinigo if (!m_start_event.Report(m_report_callback))
152bff2b9adSWalter Erquinigo return false;
153bff2b9adSWalter Erquinigo
154bff2b9adSWalter Erquinigo if (m_last_update_event)
155bff2b9adSWalter Erquinigo m_last_update_event->Report(m_report_callback);
156bff2b9adSWalter Erquinigo return true;
157bff2b9adSWalter Erquinigo }
158bff2b9adSWalter Erquinigo
GetMostRecentEvent() const159bff2b9adSWalter Erquinigo const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {
160bff2b9adSWalter Erquinigo return m_last_update_event ? *m_last_update_event : m_start_event;
161bff2b9adSWalter Erquinigo }
162bff2b9adSWalter Erquinigo
Update(uint64_t progress_id,uint64_t completed,uint64_t total)163bff2b9adSWalter Erquinigo void ProgressEventManager::Update(uint64_t progress_id, uint64_t completed,
164bff2b9adSWalter Erquinigo uint64_t total) {
165bff2b9adSWalter Erquinigo if (Optional<ProgressEvent> event = ProgressEvent::Create(
166bff2b9adSWalter Erquinigo progress_id, None, completed, total, &GetMostRecentEvent())) {
167bff2b9adSWalter Erquinigo if (event->GetEventType() == progressEnd)
168bff2b9adSWalter Erquinigo m_finished = true;
169bff2b9adSWalter Erquinigo
170bff2b9adSWalter Erquinigo m_last_update_event = *event;
171bff2b9adSWalter Erquinigo ReportIfNeeded();
172bff2b9adSWalter Erquinigo }
173bff2b9adSWalter Erquinigo }
174bff2b9adSWalter Erquinigo
Finished() const175bff2b9adSWalter Erquinigo bool ProgressEventManager::Finished() const { return m_finished; }
176bff2b9adSWalter Erquinigo
ProgressEventReporter(ProgressEventReportCallback report_callback)177bff2b9adSWalter Erquinigo ProgressEventReporter::ProgressEventReporter(
178bff2b9adSWalter Erquinigo ProgressEventReportCallback report_callback)
179bff2b9adSWalter Erquinigo : m_report_callback(report_callback) {
180bff2b9adSWalter Erquinigo m_thread_should_exit = false;
181bff2b9adSWalter Erquinigo m_thread = std::thread([&] {
182bff2b9adSWalter Erquinigo while (!m_thread_should_exit) {
183bff2b9adSWalter Erquinigo std::this_thread::sleep_for(kUpdateProgressEventReportDelay);
184bff2b9adSWalter Erquinigo ReportStartEvents();
185bff2b9adSWalter Erquinigo }
186bff2b9adSWalter Erquinigo });
187bff2b9adSWalter Erquinigo }
188bff2b9adSWalter Erquinigo
~ProgressEventReporter()189bff2b9adSWalter Erquinigo ProgressEventReporter::~ProgressEventReporter() {
190bff2b9adSWalter Erquinigo m_thread_should_exit = true;
191bff2b9adSWalter Erquinigo m_thread.join();
192bff2b9adSWalter Erquinigo }
193bff2b9adSWalter Erquinigo
ReportStartEvents()194bff2b9adSWalter Erquinigo void ProgressEventReporter::ReportStartEvents() {
195bff2b9adSWalter Erquinigo std::lock_guard<std::mutex> locker(m_mutex);
196bff2b9adSWalter Erquinigo
197bff2b9adSWalter Erquinigo while (!m_unreported_start_events.empty()) {
198bff2b9adSWalter Erquinigo ProgressEventManagerSP event_manager = m_unreported_start_events.front();
199bff2b9adSWalter Erquinigo if (event_manager->Finished())
200bff2b9adSWalter Erquinigo m_unreported_start_events.pop();
201bff2b9adSWalter Erquinigo else if (event_manager->ReportIfNeeded())
202bff2b9adSWalter Erquinigo m_unreported_start_events
203bff2b9adSWalter Erquinigo .pop(); // we remove it from the queue as it started reporting
204bff2b9adSWalter Erquinigo // already, the Push method will be able to continue its
205bff2b9adSWalter Erquinigo // reports.
206bff2b9adSWalter Erquinigo else
207bff2b9adSWalter Erquinigo break; // If we couldn't report it, then the next event in the queue won't
208bff2b9adSWalter Erquinigo // be able as well, as it came later.
209bff2b9adSWalter Erquinigo }
210bff2b9adSWalter Erquinigo }
211bff2b9adSWalter Erquinigo
Push(uint64_t progress_id,const char * message,uint64_t completed,uint64_t total)212bff2b9adSWalter Erquinigo void ProgressEventReporter::Push(uint64_t progress_id, const char *message,
213bff2b9adSWalter Erquinigo uint64_t completed, uint64_t total) {
214bff2b9adSWalter Erquinigo std::lock_guard<std::mutex> locker(m_mutex);
215bff2b9adSWalter Erquinigo
216bff2b9adSWalter Erquinigo auto it = m_event_managers.find(progress_id);
217bff2b9adSWalter Erquinigo if (it == m_event_managers.end()) {
218bff2b9adSWalter Erquinigo if (Optional<ProgressEvent> event =
219bff2b9adSWalter Erquinigo ProgressEvent::Create(progress_id, StringRef(message), completed, total)) {
220bff2b9adSWalter Erquinigo ProgressEventManagerSP event_manager =
221bff2b9adSWalter Erquinigo std::make_shared<ProgressEventManager>(*event, m_report_callback);
222bff2b9adSWalter Erquinigo m_event_managers.insert({progress_id, event_manager});
223bff2b9adSWalter Erquinigo m_unreported_start_events.push(event_manager);
224bff2b9adSWalter Erquinigo }
225bff2b9adSWalter Erquinigo } else {
226bff2b9adSWalter Erquinigo it->second->Update(progress_id, completed, total);
227bff2b9adSWalter Erquinigo if (it->second->Finished())
228bff2b9adSWalter Erquinigo m_event_managers.erase(it);
229cc88d301SWalter Erquinigo }
230cc88d301SWalter Erquinigo }
231