@@ -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,84 @@ 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
+
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.
100
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;
101
135
}
102
- m_progress_category_map[progress_data.title ].first ++;
136
+
137
+ // Regardless of how we got here, we need to bump the reference count.
138
+ entry.refcount ++;
103
139
}
104
140
105
141
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 ) ;
142
+ std::lock_guard<std::mutex> lock (m_entries_mutex );
143
+ llvm::StringRef key = progress_data.title ;
108
144
109
- if (pos == m_progress_category_map. end ( ))
145
+ if (!m_entries. contains (key ))
110
146
return ;
111
147
112
- if (pos->second .first <= 1 ) {
113
- ReportProgress (pos->second .second , EventType::End);
114
- m_progress_category_map.erase (progress_data.title );
115
- } else {
116
- --pos->second .first ;
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); });
117
162
}
118
163
}
119
164
@@ -129,3 +174,20 @@ void ProgressManager::ReportProgress(
129
174
progress_data.debugger_id ,
130
175
Debugger::eBroadcastBitProgressCategory);
131
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
+ }
0 commit comments