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