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