@@ -35,7 +35,10 @@ Progress::Progress(std::string title, std::string details,
35
35
36
36
std::lock_guard<std::mutex> guard (m_mutex);
37
37
ReportProgress ();
38
- ProgressManager::Instance ().Increment (m_progress_data);
38
+
39
+ // Report to the ProgressManager if that subsystem is enabled.
40
+ if (ProgressManager::Enabled ())
41
+ ProgressManager::Instance ().Increment (m_progress_data);
39
42
}
40
43
41
44
Progress::~Progress () {
@@ -45,7 +48,10 @@ Progress::~Progress() {
45
48
if (!m_completed)
46
49
m_completed = m_total;
47
50
ReportProgress ();
48
- ProgressManager::Instance ().Decrement (m_progress_data);
51
+
52
+ // Report to the ProgressManager if that subsystem is enabled.
53
+ if (ProgressManager::Enabled ())
54
+ ProgressManager::Instance ().Decrement (m_progress_data);
49
55
}
50
56
51
57
void Progress::Increment (uint64_t amount,
@@ -75,45 +81,86 @@ void Progress::ReportProgress() {
75
81
}
76
82
}
77
83
78
- ProgressManager::ProgressManager () : m_progress_category_map() {}
84
+ ProgressManager::ProgressManager ()
85
+ : m_alarm(std::chrono::milliseconds(100 )), m_entries() {}
79
86
80
87
ProgressManager::~ProgressManager () {}
81
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
+
82
101
ProgressManager &ProgressManager::Instance () {
83
- static std::once_flag g_once_flag ;
84
- static ProgressManager *g_progress_manager = nullptr ;
85
- std::call_once (g_once_flag, []() {
86
- // NOTE: known leak to avoid global destructor chain issues.
87
- g_progress_manager = new ProgressManager ();
88
- }) ;
89
- return * g_progress_manager;
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;
90
109
}
91
110
92
111
void ProgressManager::Increment (const Progress::ProgressData &progress_data) {
93
- std::lock_guard<std::mutex> lock (m_progress_map_mutex);
94
- // If the current category exists in the map then it is not an initial report,
95
- // therefore don't broadcast to the category bit. Also, store the current
96
- // progress data in the map so that we have a note of the ID used for the
97
- // initial progress report.
98
- if (!m_progress_category_map.contains (progress_data.title )) {
99
- m_progress_category_map[progress_data.title ].second = progress_data;
112
+ std::lock_guard<std::mutex> lock (m_entries_mutex);
113
+ llvm::StringRef key = progress_data.title ;
114
+
115
+ // This is a new progress event.
116
+ if (!m_entries.contains (key)) {
100
117
ReportProgress (progress_data, EventType::Begin);
118
+ Entry &entry = m_entries[key];
119
+ entry.data = progress_data;
120
+ entry.refcount = 1 ;
121
+ return ;
122
+ }
123
+
124
+ // This is an existing progress event.
125
+ Entry &entry = m_entries[key];
126
+
127
+ // The progress event was scheduled to be deleted but a new one came in before
128
+ // the timer expired.
129
+ if (entry.refcount == 0 ) {
130
+ assert (entry.handle != Alarm::INVALID_HANDLE);
131
+
132
+ if (!m_alarm.Cancel (entry.handle )) {
133
+ // The timer expired before we had a chance to cancel it. We have to treat
134
+ // this as an entirely new progress event.
135
+ ReportProgress (progress_data, EventType::Begin);
136
+ }
137
+ entry.refcount = 1 ;
138
+ entry.handle = Alarm::INVALID_HANDLE;
139
+ } else {
140
+ entry.refcount ++;
101
141
}
102
- m_progress_category_map[progress_data.title ].first ++;
103
142
}
104
143
105
144
void ProgressManager::Decrement (const Progress::ProgressData &progress_data) {
106
- std::lock_guard<std::mutex> lock (m_progress_map_mutex );
107
- auto pos = m_progress_category_map. find ( progress_data.title ) ;
145
+ std::lock_guard<std::mutex> lock (m_entries_mutex );
146
+ llvm::StringRef key = progress_data.title ;
108
147
109
- if (pos == m_progress_category_map. end ( ))
148
+ if (!m_entries. contains (key ))
110
149
return ;
111
150
112
- if (pos->second .first <= 1 ) {
113
- ReportProgress (pos->second .second , EventType::End);
114
- m_progress_category_map.erase (progress_data.title );
151
+ Entry &entry = m_entries[key];
152
+ if (entry.refcount <= 1 ) {
153
+ assert (entry.handle == Alarm::INVALID_HANDLE);
154
+ // Start a timer. If it expires before we see another progress event, it
155
+ // will be reported.
156
+ entry.refcount = 0 ;
157
+ // Copy the key to a std::string so we can pass it by value to the lambda.
158
+ // The underlying StringRef will not exist by the time the callback is
159
+ // called.
160
+ std::string key_str = std::string (key);
161
+ entry.handle = m_alarm.Create ([=]() { Expire (key_str); });
115
162
} else {
116
- --pos-> second . first ;
163
+ entry. refcount -- ;
117
164
}
118
165
}
119
166
@@ -129,3 +176,20 @@ void ProgressManager::ReportProgress(
129
176
progress_data.debugger_id ,
130
177
Debugger::eBroadcastBitProgressCategory);
131
178
}
179
+
180
+ void ProgressManager::Expire (llvm::StringRef key) {
181
+ std::lock_guard<std::mutex> lock (m_entries_mutex);
182
+
183
+ // This shouldn't happen but be resilient anyway.
184
+ if (!m_entries.contains (key))
185
+ return ;
186
+
187
+ // A new event came in and the alarm fired before we had a chance to restart
188
+ // it.
189
+ if (m_entries[key].refcount != 0 )
190
+ return ;
191
+
192
+ // We're done with this entry.
193
+ ReportProgress (m_entries[key].data , EventType::End);
194
+ m_entries.erase (key);
195
+ }
0 commit comments