Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/lldb/source/Core/Progress.cpp
39587 views
1
//===-- Progress.cpp ------------------------------------------------------===//
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-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include "lldb/Core/Progress.h"
10
11
#include "lldb/Core/Debugger.h"
12
#include "lldb/Utility/StreamString.h"
13
14
#include <cstdint>
15
#include <mutex>
16
#include <optional>
17
18
using namespace lldb;
19
using namespace lldb_private;
20
21
std::atomic<uint64_t> Progress::g_id(0);
22
23
Progress::Progress(std::string title, std::string details,
24
std::optional<uint64_t> total,
25
lldb_private::Debugger *debugger)
26
: m_details(details), m_completed(0),
27
m_total(Progress::kNonDeterministicTotal),
28
m_progress_data{title, ++g_id,
29
/*m_progress_data.debugger_id=*/std::nullopt} {
30
if (total)
31
m_total = *total;
32
33
if (debugger)
34
m_progress_data.debugger_id = debugger->GetID();
35
36
std::lock_guard<std::mutex> guard(m_mutex);
37
ReportProgress();
38
39
// Report to the ProgressManager if that subsystem is enabled.
40
if (ProgressManager::Enabled())
41
ProgressManager::Instance().Increment(m_progress_data);
42
}
43
44
Progress::~Progress() {
45
// Make sure to always report progress completed when this object is
46
// destructed so it indicates the progress dialog/activity should go away.
47
std::lock_guard<std::mutex> guard(m_mutex);
48
if (!m_completed)
49
m_completed = m_total;
50
ReportProgress();
51
52
// Report to the ProgressManager if that subsystem is enabled.
53
if (ProgressManager::Enabled())
54
ProgressManager::Instance().Decrement(m_progress_data);
55
}
56
57
void Progress::Increment(uint64_t amount,
58
std::optional<std::string> updated_detail) {
59
if (amount > 0) {
60
std::lock_guard<std::mutex> guard(m_mutex);
61
if (updated_detail)
62
m_details = std::move(updated_detail.value());
63
// Watch out for unsigned overflow and make sure we don't increment too
64
// much and exceed the total.
65
if (m_total && (amount > (m_total - m_completed)))
66
m_completed = m_total;
67
else
68
m_completed += amount;
69
ReportProgress();
70
}
71
}
72
73
void Progress::ReportProgress() {
74
if (!m_complete) {
75
// Make sure we only send one notification that indicates the progress is
76
// complete
77
m_complete = m_completed == m_total;
78
Debugger::ReportProgress(m_progress_data.progress_id, m_progress_data.title,
79
m_details, m_completed, m_total,
80
m_progress_data.debugger_id);
81
}
82
}
83
84
ProgressManager::ProgressManager()
85
: m_entries(), m_alarm(std::chrono::milliseconds(100)) {}
86
87
ProgressManager::~ProgressManager() {}
88
89
void ProgressManager::Initialize() {
90
assert(!InstanceImpl() && "Already initialized.");
91
InstanceImpl().emplace();
92
}
93
94
void ProgressManager::Terminate() {
95
assert(InstanceImpl() && "Already terminated.");
96
InstanceImpl().reset();
97
}
98
99
bool ProgressManager::Enabled() { return InstanceImpl().operator bool(); }
100
101
ProgressManager &ProgressManager::Instance() {
102
assert(InstanceImpl() && "ProgressManager must be initialized");
103
return *InstanceImpl();
104
}
105
106
std::optional<ProgressManager> &ProgressManager::InstanceImpl() {
107
static std::optional<ProgressManager> g_progress_manager;
108
return g_progress_manager;
109
}
110
111
void ProgressManager::Increment(const Progress::ProgressData &progress_data) {
112
std::lock_guard<std::mutex> lock(m_entries_mutex);
113
114
llvm::StringRef key = progress_data.title;
115
bool new_entry = !m_entries.contains(key);
116
Entry &entry = m_entries[progress_data.title];
117
118
if (new_entry) {
119
// This is a new progress event. Report progress and store the progress
120
// data.
121
ReportProgress(progress_data, EventType::Begin);
122
entry.data = progress_data;
123
} else if (entry.refcount == 0) {
124
// This is an existing entry that was scheduled to be deleted but a new one
125
// came in before the timer expired.
126
assert(entry.handle != Alarm::INVALID_HANDLE);
127
128
if (!m_alarm.Cancel(entry.handle)) {
129
// The timer expired before we had a chance to cancel it. We have to treat
130
// this as an entirely new progress event.
131
ReportProgress(progress_data, EventType::Begin);
132
}
133
// Clear the alarm handle.
134
entry.handle = Alarm::INVALID_HANDLE;
135
}
136
137
// Regardless of how we got here, we need to bump the reference count.
138
entry.refcount++;
139
}
140
141
void ProgressManager::Decrement(const Progress::ProgressData &progress_data) {
142
std::lock_guard<std::mutex> lock(m_entries_mutex);
143
llvm::StringRef key = progress_data.title;
144
145
if (!m_entries.contains(key))
146
return;
147
148
Entry &entry = m_entries[key];
149
entry.refcount--;
150
151
if (entry.refcount == 0) {
152
assert(entry.handle == Alarm::INVALID_HANDLE);
153
154
// Copy the key to a std::string so we can pass it by value to the lambda.
155
// The underlying StringRef will not exist by the time the callback is
156
// called.
157
std::string key_str = std::string(key);
158
159
// Start a timer. If it expires before we see another progress event, it
160
// will be reported.
161
entry.handle = m_alarm.Create([=]() { Expire(key_str); });
162
}
163
}
164
165
void ProgressManager::ReportProgress(
166
const Progress::ProgressData &progress_data, EventType type) {
167
// The category bit only keeps track of when progress report categories have
168
// started and ended, so clear the details and reset other fields when
169
// broadcasting to it since that bit doesn't need that information.
170
const uint64_t completed =
171
(type == EventType::Begin) ? 0 : Progress::kNonDeterministicTotal;
172
Debugger::ReportProgress(progress_data.progress_id, progress_data.title, "",
173
completed, Progress::kNonDeterministicTotal,
174
progress_data.debugger_id,
175
lldb::eBroadcastBitProgressCategory);
176
}
177
178
void ProgressManager::Expire(llvm::StringRef key) {
179
std::lock_guard<std::mutex> lock(m_entries_mutex);
180
181
// This shouldn't happen but be resilient anyway.
182
if (!m_entries.contains(key))
183
return;
184
185
// A new event came in and the alarm fired before we had a chance to restart
186
// it.
187
if (m_entries[key].refcount != 0)
188
return;
189
190
// We're done with this entry.
191
ReportProgress(m_entries[key].data, EventType::End);
192
m_entries.erase(key);
193
}
194
195