brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.9 KiB · e53ec40 Raw
238 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "ProgressEvent.h"10 11#include "JSONUtils.h"12#include "llvm/Support/ErrorHandling.h"13#include <optional>14 15using namespace lldb_dap;16using namespace llvm;17 18// The minimum duration of an event for it to be reported19const std::chrono::duration<double> kStartProgressEventReportDelay =20    std::chrono::seconds(1);21// The minimum time interval between update events for reporting. If multiple22// updates fall within the same time interval, only the latest is reported.23const std::chrono::duration<double> kUpdateProgressEventReportDelay =24    std::chrono::milliseconds(250);25 26ProgressEvent::ProgressEvent(uint64_t progress_id,27                             std::optional<StringRef> message,28                             uint64_t completed, uint64_t total,29                             const ProgressEvent *prev_event)30    : m_progress_id(progress_id) {31  if (message)32    m_message = message->str();33 34  const bool calculate_percentage = total != UINT64_MAX;35  if (completed == 0) {36    // Start event37    m_event_type = progressStart;38    // Wait a bit before reporting the start event in case in completes really39    // quickly.40    m_minimum_allowed_report_time =41        m_creation_time + kStartProgressEventReportDelay;42    if (calculate_percentage)43      m_percentage = 0;44  } else if (completed == total) {45    // End event46    m_event_type = progressEnd;47    // We should report the end event right away.48    m_minimum_allowed_report_time = std::chrono::seconds::zero();49    if (calculate_percentage)50      m_percentage = 100;51  } else {52    // Update event53    m_event_type = progressUpdate;54    m_percentage = std::min(55        (uint32_t)((double)completed / (double)total * 100.0), (uint32_t)99);56    if (prev_event->Reported()) {57      // Add a small delay between reports58      m_minimum_allowed_report_time =59          prev_event->m_minimum_allowed_report_time +60          kUpdateProgressEventReportDelay;61    } else {62      // We should use the previous timestamp, as it's still pending63      m_minimum_allowed_report_time = prev_event->m_minimum_allowed_report_time;64    }65  }66}67 68std::optional<ProgressEvent>69ProgressEvent::Create(uint64_t progress_id, std::optional<StringRef> message,70                      uint64_t completed, uint64_t total,71                      const ProgressEvent *prev_event) {72  // If it's an update without a previous event, we abort73  if (completed > 0 && completed < total && !prev_event)74    return std::nullopt;75  ProgressEvent event(progress_id, message, completed, total, prev_event);76  // We shouldn't show unnamed start events in the IDE77  if (event.GetEventType() == progressStart && event.GetEventName().empty())78    return std::nullopt;79 80  if (prev_event && prev_event->EqualsForIDE(event))81    return std::nullopt;82 83  return event;84}85 86bool ProgressEvent::EqualsForIDE(const ProgressEvent &other) const {87  return m_progress_id == other.m_progress_id &&88         m_event_type == other.m_event_type &&89         m_percentage == other.m_percentage && m_message == other.m_message;90}91 92ProgressEventType ProgressEvent::GetEventType() const { return m_event_type; }93 94StringRef ProgressEvent::GetEventName() const {95  switch (m_event_type) {96  case progressStart:97    return "progressStart";98  case progressUpdate:99    return "progressUpdate";100  case progressEnd:101    return "progressEnd";102  }103  llvm_unreachable("All cases handled above!");104}105 106json::Value ProgressEvent::ToJSON() const {107  llvm::json::Object event(CreateEventObject(GetEventName()));108  llvm::json::Object body;109 110  std::string progress_id_str;111  llvm::raw_string_ostream progress_id_strm(progress_id_str);112  progress_id_strm << m_progress_id;113  body.try_emplace("progressId", progress_id_str);114 115  if (m_event_type == progressStart) {116    EmplaceSafeString(body, "title", m_message);117    body.try_emplace("cancellable", false);118  }119 120  if (m_event_type == progressUpdate)121    EmplaceSafeString(body, "message", m_message);122 123  std::string timestamp(llvm::formatv("{0:f9}", m_creation_time.count()));124  EmplaceSafeString(body, "timestamp", timestamp);125 126  if (m_percentage)127    body.try_emplace("percentage", *m_percentage);128 129  event.try_emplace("body", std::move(body));130  return json::Value(std::move(event));131}132 133bool ProgressEvent::Report(ProgressEventReportCallback callback) {134  if (Reported())135    return true;136  if (std::chrono::system_clock::now().time_since_epoch() <137      m_minimum_allowed_report_time)138    return false;139 140  m_reported = true;141  callback(*this);142  return true;143}144 145bool ProgressEvent::Reported() const { return m_reported; }146 147ProgressEventManager::ProgressEventManager(148    const ProgressEvent &start_event,149    ProgressEventReportCallback report_callback)150    : m_start_event(start_event), m_finished(false),151      m_report_callback(report_callback) {}152 153bool ProgressEventManager::ReportIfNeeded() {154  // The event finished before we were able to report it.155  if (!m_start_event.Reported() && Finished())156    return true;157 158  if (!m_start_event.Report(m_report_callback))159    return false;160 161  if (m_last_update_event)162    m_last_update_event->Report(m_report_callback);163  return true;164}165 166const ProgressEvent &ProgressEventManager::GetMostRecentEvent() const {167  return m_last_update_event ? *m_last_update_event : m_start_event;168}169 170void ProgressEventManager::Update(uint64_t progress_id, llvm::StringRef message,171                                  uint64_t completed, uint64_t total) {172  if (std::optional<ProgressEvent> event = ProgressEvent::Create(173          progress_id, message, completed, total, &GetMostRecentEvent())) {174    if (event->GetEventType() == progressEnd)175      m_finished = true;176 177    m_last_update_event = *event;178    ReportIfNeeded();179  }180}181 182bool ProgressEventManager::Finished() const { return m_finished; }183 184ProgressEventReporter::ProgressEventReporter(185    ProgressEventReportCallback report_callback)186    : m_report_callback(report_callback) {187  m_thread_should_exit = false;188  m_thread = std::thread([&] {189    while (!m_thread_should_exit) {190      std::this_thread::sleep_for(kUpdateProgressEventReportDelay);191      ReportStartEvents();192    }193  });194}195 196ProgressEventReporter::~ProgressEventReporter() {197  m_thread_should_exit = true;198  m_thread.join();199}200 201void ProgressEventReporter::ReportStartEvents() {202  std::lock_guard<std::mutex> locker(m_mutex);203 204  while (!m_unreported_start_events.empty()) {205    ProgressEventManagerSP event_manager = m_unreported_start_events.front();206    if (event_manager->Finished())207      m_unreported_start_events.pop();208    else if (event_manager->ReportIfNeeded())209      m_unreported_start_events210          .pop(); // we remove it from the queue as it started reporting211                  // already, the Push method will be able to continue its212                  // reports.213    else214      break; // If we couldn't report it, then the next event in the queue won't215             // be able as well, as it came later.216  }217}218 219void ProgressEventReporter::Push(uint64_t progress_id, const char *message,220                                 uint64_t completed, uint64_t total) {221  std::lock_guard<std::mutex> locker(m_mutex);222 223  auto it = m_event_managers.find(progress_id);224  if (it == m_event_managers.end()) {225    if (std::optional<ProgressEvent> event = ProgressEvent::Create(226            progress_id, StringRef(message), completed, total)) {227      ProgressEventManagerSP event_manager =228          std::make_shared<ProgressEventManager>(*event, m_report_callback);229      m_event_managers.insert({progress_id, event_manager});230      m_unreported_start_events.push(event_manager);231    }232  } else {233    it->second->Update(progress_id, StringRef(message), completed, total);234    if (it->second->Finished())235      m_event_managers.erase(it);236  }237}238