wallace updated this revision to Diff 342386.
wallace marked 12 inline comments as done.
wallace added a comment.
Address all comments
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D101128/new/
https://reviews.llvm.org/D101128
Files:
lldb/tools/lldb-vscode/ProgressEvent.cpp
lldb/tools/lldb-vscode/ProgressEvent.h
lldb/tools/lldb-vscode/VSCode.h
Index: lldb/tools/lldb-vscode/VSCode.h
===================================================================
--- lldb/tools/lldb-vscode/VSCode.h
+++ lldb/tools/lldb-vscode/VSCode.h
@@ -125,10 +125,6 @@
int64_t GetLineForPC(int64_t sourceReference, lldb::addr_t pc) const;
ExceptionBreakpoint *GetExceptionBreakpoint(const std::string &filter);
ExceptionBreakpoint *GetExceptionBreakpoint(const lldb::break_id_t bp_id);
- // Send the JSON in "json_str" to the "out" stream. Correctly send the
- // "Content-Length:" field followed by the length, followed by the raw
- // JSON bytes.
- void SendJSON(const std::string &json_str);
// Serialize the JSON value into a string and send the JSON packet to
// the "out" stream.
@@ -208,6 +204,12 @@
/// The callback to execute when the given request is triggered by the
/// IDE.
void RegisterRequestCallback(std::string request, RequestCallback callback);
+
+private:
+ // Send the JSON in "json_str" to the "out" stream. Correctly send the
+ // "Content-Length:" field followed by the length, followed by the raw
+ // JSON bytes.
+ void SendJSON(const std::string &json_str);
};
extern VSCode g_vsc;
Index: lldb/tools/lldb-vscode/ProgressEvent.h
===================================================================
--- lldb/tools/lldb-vscode/ProgressEvent.h
+++ lldb/tools/lldb-vscode/ProgressEvent.h
@@ -6,6 +6,10 @@
//
//===----------------------------------------------------------------------===//
+#include <mutex>
+#include <queue>
+#include <thread>
+
#include "VSCodeForward.h"
#include "llvm/Support/JSON.h"
@@ -28,35 +32,108 @@
llvm::json::Value ToJSON() const;
- /// This operator returns \b true if two event messages
- /// would result in the same event for the IDE, e.g.
- /// same rounded percentage.
- bool operator==(const ProgressEvent &other) const;
+ /// \return
+ /// \b true if two event messages would result in the same event for the
+ /// IDE, e.g. same rounded percentage.
+ bool EqualsForIDE(const ProgressEvent &other) const;
const char *GetEventName() const;
+ ProgressEventType GetEventType() const;
+
bool IsValid() const;
uint64_t GetID() const;
+ void MarkAsReported();
+
+ bool Reported() const;
+
+ std::chrono::duration<double> GetTimestamp() const;
+
private:
uint64_t m_progress_id;
- const char *m_message;
+ std::string m_message;
ProgressEventType m_event_type;
llvm::Optional<uint32_t> m_percentage;
+ std::chrono::duration<double> m_timestamp;
+ bool m_reported;
+};
+
+using ProgressEventReportCallback = std::function<void(ProgressEvent &)>;
+
+/// Class that keeps the start event and its most recent update.
+/// It controls when the event should start being reported to the IDE.
+class ProgressEventManager {
+public:
+ ProgressEventManager(const ProgressEvent &start_event,
+ ProgressEventReportCallback report_callback);
+
+ /// Report the start event and the most recent update if the event has lasted
+ /// for long enough.
+ ///
+ /// \return
+ /// \b false if the event hasn't finished and hasn't reported anything
+ /// yet.
+ bool TryReport();
+
+ /// Receive a new progress event for the start event and try to report it if
+ /// appropriate.
+ void Update(const ProgressEvent &event);
+
+ /// \return
+ /// \b true if a \a progressEnd event has been notified. There's no
+ /// need to try to report manually an event that has finished.
+ bool Finished() const;
+
+ /// \return
+ /// The ID of the underlying start event.
+ uint64_t GetID() const;
+
+private:
+ ProgressEvent m_start_event;
+ llvm::Optional<ProgressEvent> m_last_update_event;
+ std::chrono::duration<double> m_start_event_timestamp;
+ bool m_finished;
+ ProgressEventReportCallback m_report_callback;
};
+using ProgressEventManagerSP = std::shared_ptr<ProgressEventManager>;
+
/// Class that filters out progress event messages that shouldn't be reported
-/// to the IDE, either because they are invalid or because they are too chatty.
+/// to the IDE, because they are invalid, they carry no new information, or they
+/// don't last long enough.
+///
+/// We need to limit the amount of events that are sent to the IDE, as they slow
+/// the render thread of the UI user, and they end up spamming the DAP
+/// connection, which also takes some processing time out of the IDE.
class ProgressEventFilterQueue {
public:
- ProgressEventFilterQueue(std::function<void(ProgressEvent)> callback);
+ /// \param[in] report_callback
+ /// Function to invoke to report the event to the IDE.
+ ProgressEventFilterQueue(ProgressEventReportCallback report_callback);
+
+ ~ProgressEventFilterQueue();
+ /// Add a new event to the internal queue and reports the event if
+ /// appropriate.
void Push(const ProgressEvent &event);
private:
- std::function<void(ProgressEvent)> m_callback;
- std::map<uint64_t, ProgressEvent> m_last_events;
+ /// Report to the IDE events that haven't been reported to the IDE and have
+ /// lasted long enough.
+ void ReportWaitingEvents();
+
+ ProgressEventReportCallback m_report_callback;
+ std::map<uint64_t, ProgressEventManagerSP> m_event_managers;
+ /// Queue of event managers in chronological order
+ std::queue<ProgressEventManagerSP> m_sorted_event_managers;
+ /// Thread used to invoke \a ReportWaitingEvents periodically.
+ std::thread m_aux_reporter_thread;
+ bool m_aux_reporter_thread_should_exit;
+ /// Mutex that prevents running \a Push and \a ReportWaitingEvents
+ /// simultaneously, as both read and modify the same underlying objects.
+ std::mutex m_reporter_mutex;
};
} // namespace lldb_vscode
Index: lldb/tools/lldb-vscode/ProgressEvent.cpp
===================================================================
--- lldb/tools/lldb-vscode/ProgressEvent.cpp
+++ lldb/tools/lldb-vscode/ProgressEvent.cpp
@@ -13,28 +13,40 @@
using namespace lldb_vscode;
using namespace llvm;
-ProgressEvent::ProgressEvent(uint64_t progress_id, const char *message,
- uint64_t completed, uint64_t total)
- : m_progress_id(progress_id), m_message(message) {
+static ProgressEventType DetermineProgressEventType(uint64_t completed,
+ uint64_t total) {
if (completed == total)
- m_event_type = progressEnd;
+ return progressEnd;
else if (completed == 0)
- m_event_type = progressStart;
+ return progressStart;
else if (completed < total)
- m_event_type = progressUpdate;
+ return progressUpdate;
else
- m_event_type = progressInvalid;
+ return progressInvalid;
+}
+
+ProgressEvent::ProgressEvent(uint64_t progress_id, const char *message,
+ uint64_t completed, uint64_t total)
+ : m_progress_id(progress_id),
+ m_event_type(DetermineProgressEventType(completed, total)),
+ m_timestamp(std::chrono::system_clock::now().time_since_epoch()),
+ m_reported(false) {
+
+ if (m_event_type == progressStart)
+ m_message = message;
if (0 < total && total < UINT64_MAX)
m_percentage = (uint32_t)(((float)completed / (float)total) * 100.0);
}
-bool ProgressEvent::operator==(const ProgressEvent &other) const {
+bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {
return m_progress_id == other.m_progress_id &&
m_event_type == other.m_event_type &&
m_percentage == other.m_percentage;
}
+ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }
+
const char *ProgressEvent::GetEventName() const {
if (m_event_type == progressStart)
return "progressStart";
@@ -65,9 +77,7 @@
body.try_emplace("cancellable", false);
}
- auto now = std::chrono::duration<double>(
- std::chrono::system_clock::now().time_since_epoch());
- std::string timestamp(llvm::formatv("{0:f9}", now.count()));
+ std::string timestamp(llvm::formatv("{0:f9}", m_timestamp.count()));
EmplaceSafeString(body, "timestamp", timestamp);
if (m_percentage)
@@ -77,17 +87,106 @@
return json::Value(std::move(event));
}
+void ProgressEvent::MarkAsReported() { m_reported = true; }
+
+bool ProgressEvent::Reported() const { return m_reported; }
+
+std::chrono::duration<double> ProgressEvent::GetTimestamp() const {
+ return m_timestamp;
+}
+
+ProgressEventManager::ProgressEventManager(
+ const ProgressEvent &start_event,
+ ProgressEventReportCallback report_callback)
+ : m_start_event(start_event),
+ m_start_event_timestamp(
+ std::chrono::system_clock::now().time_since_epoch()),
+ m_finished(false), m_report_callback(report_callback) {}
+
+bool ProgressEventManager::TryReport() {
+ // We only report if we have already started reporting or if enough time has
+ // passed since the start event.
+ if (!m_start_event.Reported() &&
+ std::chrono::system_clock::now().time_since_epoch() -
+ m_start_event.GetTimestamp() <
+ std::chrono::seconds(1))
+ return false;
+
+ if (!m_start_event.Reported() && !Finished()) {
+ m_report_callback(m_start_event);
+ m_start_event.MarkAsReported();
+ }
+ if (m_last_update_event && !m_last_update_event->Reported()) {
+ m_report_callback(*m_last_update_event);
+ m_last_update_event->MarkAsReported();
+ }
+ return true;
+}
+
+void ProgressEventManager::Update(const ProgressEvent &event) {
+ if (event.GetEventType() == progressEnd)
+ m_finished = true;
+
+ if (!m_last_update_event || !event.EqualsForIDE(*m_last_update_event)) {
+ m_last_update_event = event;
+ TryReport();
+ }
+}
+
+uint64_t ProgressEventManager::GetID() const { return m_start_event.GetID(); }
+
+bool ProgressEventManager::Finished() const { return m_finished; }
+
ProgressEventFilterQueue::ProgressEventFilterQueue(
- std::function<void(ProgressEvent)> callback)
- : m_callback(callback) {}
+ ProgressEventReportCallback report_callback)
+ : m_report_callback(report_callback) {
+ m_aux_reporter_thread_should_exit = false;
+ m_aux_reporter_thread = std::thread([&] {
+ while (!m_aux_reporter_thread_should_exit) {
+ std::this_thread::sleep_for(std::chrono::seconds(2));
+ ReportWaitingEvents();
+ }
+ });
+}
+
+ProgressEventFilterQueue::~ProgressEventFilterQueue() {
+ m_aux_reporter_thread_should_exit = true;
+ m_aux_reporter_thread.join();
+}
+
+void ProgressEventFilterQueue::ReportWaitingEvents() {
+ std::lock_guard<std::mutex> locker(m_reporter_mutex);
+
+ while (!m_sorted_event_managers.empty()) {
+ ProgressEventManagerSP event_manager = m_sorted_event_managers.front();
+ if (event_manager->Finished())
+ m_sorted_event_managers.pop();
+ else if (event_manager->TryReport())
+ m_sorted_event_managers
+ .pop(); // we remove it from the queue as it started reporting
+ // already, the Push method will be able to continue its
+ // reports.
+ else
+ break; // If we couldn't report it, then the next event in the queue won't
+ // be able as well, as it came later.
+ }
+}
void ProgressEventFilterQueue::Push(const ProgressEvent &event) {
if (!event.IsValid())
return;
- auto it = m_last_events.find(event.GetID());
- if (it == m_last_events.end() || !(it->second == event)) {
- m_last_events[event.GetID()] = event;
- m_callback(event);
+ std::lock_guard<std::mutex> locker(m_reporter_mutex);
+
+ auto it = m_event_managers.find(event.GetID());
+ if (it == m_event_managers.end()) {
+ ProgressEventManagerSP event_manager =
+ std::make_shared<ProgressEventManager>(event, m_report_callback);
+ m_event_managers.insert({event.GetID(), event_manager});
+ m_sorted_event_managers.push(event_manager);
+ } else {
+ it->second->Update(event);
+ if (it->second->Finished())
+ m_event_managers.erase(it);
}
}
_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits