[Lldb-commits] [lldb] a1b14db - [lldb] Remove progress report coalescing (#130329)
via lldb-commits
lldb-commits at lists.llvm.org
Sat Mar 8 09:10:50 PST 2025
Author: Jonas Devlieghere
Date: 2025-03-08T09:10:46-08:00
New Revision: a1b14dbc4740fe49dfb2af44907a195597474556
URL: https://github.com/llvm/llvm-project/commit/a1b14dbc4740fe49dfb2af44907a195597474556
DIFF: https://github.com/llvm/llvm-project/commit/a1b14dbc4740fe49dfb2af44907a195597474556.diff
LOG: [lldb] Remove progress report coalescing (#130329)
Remove support for coalescing progress reports in LLDB. This
functionality was motivated by Xcode, which wanted to listen for less
frequent, aggregated progress events at the cost of losing some detail.
See the original RFC [1] for more details. Since then, they've
reevaluated this trade-off and opted to listen for the regular, full
fidelity progress events and do any post processing on their end.
rdar://146425487
Added:
Modified:
lldb/include/lldb/Core/Progress.h
lldb/include/lldb/lldb-enumerations.h
lldb/source/API/SystemInitializerFull.cpp
lldb/source/Core/Debugger.cpp
lldb/source/Core/Progress.cpp
lldb/source/Host/CMakeLists.txt
lldb/test/API/python_api/sbprogress/TestSBProgress.py
lldb/unittests/Callback/TestBreakpointSetCallback.cpp
lldb/unittests/Core/ProgressReportTest.cpp
lldb/unittests/Host/CMakeLists.txt
Removed:
lldb/include/lldb/Host/Alarm.h
lldb/source/Host/common/Alarm.cpp
lldb/unittests/Host/AlarmTest.cpp
################################################################################
diff --git a/lldb/include/lldb/Core/Progress.h b/lldb/include/lldb/Core/Progress.h
index 5876eae717e96..3003568e8946b 100644
--- a/lldb/include/lldb/Core/Progress.h
+++ b/lldb/include/lldb/Core/Progress.h
@@ -9,7 +9,6 @@
#ifndef LLDB_CORE_PROGRESS_H
#define LLDB_CORE_PROGRESS_H
-#include "lldb/Host/Alarm.h"
#include "lldb/Utility/Timeout.h"
#include "lldb/lldb-forward.h"
#include "lldb/lldb-types.h"
@@ -18,6 +17,7 @@
#include <cstdint>
#include <mutex>
#include <optional>
+#include <string>
namespace lldb_private {
@@ -115,21 +115,6 @@ class Progress {
/// Used to indicate a non-deterministic progress report
static constexpr uint64_t kNonDeterministicTotal = UINT64_MAX;
- /// Data belonging to this Progress event that is used for bookkeeping by
- /// ProgressManager.
- struct ProgressData {
- /// The title of the progress activity, also used as a category.
- std::string title;
- /// A unique integer identifier for progress reporting.
- uint64_t progress_id;
- /// The optional debugger ID to report progress to. If this has no value
- /// then all debuggers will receive this event.
- std::optional<lldb::user_id_t> debugger_id;
-
- /// The origin of the progress event, wheter it is internal or external.
- Origin origin;
- };
-
private:
void ReportProgress();
static std::atomic<uint64_t> g_id;
@@ -141,8 +126,18 @@ class Progress {
// Minimum amount of time between two progress reports.
const Timeout<std::nano> m_minimum_report_time;
- /// Data needed by the debugger to broadcast a progress event.
- const ProgressData m_progress_data;
+ /// The title of the progress activity, also used as a category.
+ const std::string m_title;
+
+ /// A unique integer identifier for progress reporting.
+ const uint64_t m_progress_id;
+
+ /// The optional debugger ID to report progress to. If this has no value
+ /// then all debuggers will receive this event.
+ const std::optional<lldb::user_id_t> m_debugger_id;
+
+ /// The origin of the progress event, whether it is internal or external.
+ const Origin m_origin;
/// How much work ([0...m_total]) that has been completed.
std::atomic<uint64_t> m_completed = 0;
@@ -161,60 +156,6 @@ class Progress {
std::optional<uint64_t> m_prev_completed;
};
-/// A class used to group progress reports by category. This is done by using a
-/// map that maintains a refcount of each category of progress reports that have
-/// come in. Keeping track of progress reports this way will be done if a
-/// debugger is listening to the eBroadcastBitProgressByCategory broadcast bit.
-class ProgressManager {
-public:
- ProgressManager();
- ~ProgressManager();
-
- /// Control the refcount of the progress report category as needed.
- void Increment(const Progress::ProgressData &);
- void Decrement(const Progress::ProgressData &);
-
- static void Initialize();
- static void Terminate();
- static bool Enabled();
- static ProgressManager &Instance();
-
-protected:
- enum class EventType {
- Begin,
- End,
- };
- static void ReportProgress(const Progress::ProgressData &progress_data,
- EventType type);
-
- static std::optional<ProgressManager> &InstanceImpl();
-
- /// Helper function for reporting progress when the alarm in the corresponding
- /// entry in the map expires.
- void Expire(llvm::StringRef key);
-
- /// Entry used for bookkeeping.
- struct Entry {
- /// Reference count used for overlapping events.
- uint64_t refcount = 0;
-
- /// Data used to emit progress events.
- Progress::ProgressData data;
-
- /// Alarm handle used when the refcount reaches zero.
- Alarm::Handle handle = Alarm::INVALID_HANDLE;
- };
-
- /// Map used for bookkeeping.
- llvm::StringMap<Entry> m_entries;
-
- /// Mutex to provide the map.
- std::mutex m_entries_mutex;
-
- /// Alarm instance to coalesce progress events.
- Alarm m_alarm;
-};
-
} // namespace lldb_private
#endif // LLDB_CORE_PROGRESS_H
diff --git a/lldb/include/lldb/Host/Alarm.h b/lldb/include/lldb/Host/Alarm.h
deleted file mode 100644
index 23b1ff1af5689..0000000000000
--- a/lldb/include/lldb/Host/Alarm.h
+++ /dev/null
@@ -1,115 +0,0 @@
-//===-- Alarm.h -------------------------------------------------*- C++ -*-===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLDB_HOST_ALARM_H
-#define LLDB_HOST_ALARM_H
-
-#include "lldb/Host/HostThread.h"
-#include "lldb/lldb-types.h"
-#include "llvm/Support/Chrono.h"
-
-#include <condition_variable>
-#include <mutex>
-
-namespace lldb_private {
-
-/// \class Alarm abstraction that enables scheduling a callback function after a
-/// specified timeout. Creating an alarm for a callback returns a Handle that
-/// can be used to restart or cancel the alarm.
-class Alarm {
-public:
- using Handle = uint64_t;
- using Callback = std::function<void()>;
- using TimePoint = llvm::sys::TimePoint<>;
- using Duration = std::chrono::milliseconds;
-
- Alarm(Duration timeout, bool run_callback_on_exit = false);
- ~Alarm();
-
- /// Create an alarm for the given callback. The alarm will expire and the
- /// callback will be called after the timeout.
- ///
- /// \returns
- /// Handle which can be used to restart or cancel the alarm.
- Handle Create(Callback callback);
-
- /// Restart the alarm for the given Handle. The alarm will expire and the
- /// callback will be called after the timeout.
- ///
- /// \returns
- /// True if the alarm was successfully restarted. False if there is no alarm
- /// for the given Handle or the alarm already expired.
- bool Restart(Handle handle);
-
- /// Cancel the alarm for the given Handle. The alarm and its handle will be
- /// removed.
- ///
- /// \returns
- /// True if the alarm was successfully canceled and the Handle removed.
- /// False if there is no alarm for the given Handle or the alarm already
- /// expired.
- bool Cancel(Handle handle);
-
- static constexpr Handle INVALID_HANDLE = 0;
-
-private:
- /// Helper functions to start, stop and check the status of the alarm thread.
- /// @{
- void StartAlarmThread();
- void StopAlarmThread();
- bool AlarmThreadRunning();
- /// @}
-
- /// Return an unique, monotonically increasing handle.
- static Handle GetNextUniqueHandle();
-
- /// Helper to compute the next time the alarm thread needs to wake up.
- TimePoint GetNextExpiration() const;
-
- /// Alarm entry.
- struct Entry {
- Handle handle;
- Callback callback;
- TimePoint expiration;
-
- Entry(Callback callback, TimePoint expiration);
- bool operator==(const Entry &rhs) { return handle == rhs.handle; }
- };
-
- /// List of alarm entries.
- std::vector<Entry> m_entries;
-
- /// Timeout between when an alarm is created and when it fires.
- Duration m_timeout;
-
- /// The alarm thread.
- /// @{
- HostThread m_alarm_thread;
- lldb::thread_result_t AlarmThread();
- /// @}
-
- /// Synchronize access between the alarm thread and the main thread.
- std::mutex m_alarm_mutex;
-
- /// Condition variable used to wake up the alarm thread.
- std::condition_variable m_alarm_cv;
-
- /// Flag to signal the alarm thread that something changed and we need to
- /// recompute the next alarm.
- bool m_recompute_next_alarm = false;
-
- /// Flag to signal the alarm thread to exit.
- bool m_exit = false;
-
- /// Flag to signal we should run all callbacks on exit.
- bool m_run_callbacks_on_exit = false;
-};
-
-} // namespace lldb_private
-
-#endif // LLDB_HOST_ALARM_H
diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h
index fecf9cbb765f7..e30ce5be57b69 100644
--- a/lldb/include/lldb/lldb-enumerations.h
+++ b/lldb/include/lldb/lldb-enumerations.h
@@ -1356,9 +1356,9 @@ enum DebuggerBroadcastBit {
eBroadcastBitWarning = (1 << 1),
eBroadcastBitError = (1 << 2),
eBroadcastSymbolChange = (1 << 3),
- eBroadcastBitProgressCategory = (1 << 4),
+ eBroadcastBitProgressCategory = (1 << 4), ///< Deprecated
eBroadcastBitExternalProgress = (1 << 5),
- eBroadcastBitExternalProgressCategory = (1 << 6),
+ eBroadcastBitExternalProgressCategory = (1 << 6), ///< Deprecated
};
/// Used for expressing severity in logs and diagnostics.
diff --git a/lldb/source/API/SystemInitializerFull.cpp b/lldb/source/API/SystemInitializerFull.cpp
index 31f3a9f30b81f..9cc3779d1895f 100644
--- a/lldb/source/API/SystemInitializerFull.cpp
+++ b/lldb/source/API/SystemInitializerFull.cpp
@@ -68,9 +68,6 @@ llvm::Error SystemInitializerFull::Initialize() {
const char *arg0 = "lldb";
llvm::cl::ParseCommandLineOptions(1, &arg0);
- // Initialize the progress manager.
- ProgressManager::Initialize();
-
#define LLDB_PLUGIN(p) LLDB_PLUGIN_INITIALIZE(p);
#include "Plugins/Plugins.def"
@@ -104,9 +101,6 @@ void SystemInitializerFull::Terminate() {
#define LLDB_PLUGIN(p) LLDB_PLUGIN_TERMINATE(p);
#include "Plugins/Plugins.def"
- // Terminate the progress manager.
- ProgressManager::Terminate();
-
// Now shutdown the common parts, in reverse order.
SystemInitializerCommon::Terminate();
}
diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp
index dbe3d72e5efa4..8c705f889983a 100644
--- a/lldb/source/Core/Debugger.cpp
+++ b/lldb/source/Core/Debugger.cpp
@@ -1548,7 +1548,7 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
std::string details, uint64_t completed,
uint64_t total,
std::optional<lldb::user_id_t> debugger_id,
- uint32_t progress_category_bit) {
+ uint32_t progress_broadcast_bit) {
// Check if this progress is for a specific debugger.
if (debugger_id) {
// It is debugger specific, grab it and deliver the event if the debugger
@@ -1558,7 +1558,7 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
PrivateReportProgress(*debugger_sp, progress_id, std::move(title),
std::move(details), completed, total,
/*is_debugger_specific*/ true,
- progress_category_bit);
+ progress_broadcast_bit);
return;
}
// The progress event is not debugger specific, iterate over all debuggers
@@ -1569,7 +1569,7 @@ void Debugger::ReportProgress(uint64_t progress_id, std::string title,
for (pos = g_debugger_list_ptr->begin(); pos != end; ++pos)
PrivateReportProgress(*(*pos), progress_id, title, details, completed,
total, /*is_debugger_specific*/ false,
- progress_category_bit);
+ progress_broadcast_bit);
}
}
diff --git a/lldb/source/Core/Progress.cpp b/lldb/source/Core/Progress.cpp
index 63f9804320809..d29dce0a688c0 100644
--- a/lldb/source/Core/Progress.cpp
+++ b/lldb/source/Core/Progress.cpp
@@ -31,11 +31,11 @@ Progress::Progress(std::string title, std::string details,
Timeout<std::nano> minimum_report_time,
Progress::Origin origin)
: m_total(total.value_or(Progress::kNonDeterministicTotal)),
- m_minimum_report_time(minimum_report_time),
- m_progress_data{title, ++g_id,
- debugger ? std::optional<user_id_t>(debugger->GetID())
- : std::nullopt,
- origin},
+ m_minimum_report_time(minimum_report_time), m_title(title),
+ m_progress_id(++g_id),
+ m_debugger_id(debugger ? std::optional<user_id_t>(debugger->GetID())
+ : std::nullopt),
+ m_origin(origin),
m_last_report_time_ns(
std::chrono::nanoseconds(
std::chrono::steady_clock::now().time_since_epoch())
@@ -44,27 +44,19 @@ Progress::Progress(std::string title, std::string details,
std::lock_guard<std::mutex> guard(m_mutex);
ReportProgress();
- // Report to the ProgressManager if that subsystem is enabled.
- if (ProgressManager::Enabled())
- ProgressManager::Instance().Increment(m_progress_data);
-
// Start signpost interval right before the meaningful work starts.
- g_progress_signposts->startInterval(this, m_progress_data.title);
+ g_progress_signposts->startInterval(this, m_title);
}
Progress::~Progress() {
// End signpost interval as soon as possible.
- g_progress_signposts->endInterval(this, m_progress_data.title);
+ g_progress_signposts->endInterval(this, m_title);
// Make sure to always report progress completed when this object is
// destructed so it indicates the progress dialog/activity should go away.
std::lock_guard<std::mutex> guard(m_mutex);
m_completed = m_total;
ReportProgress();
-
- // Report to the ProgressManager if that subsystem is enabled.
- if (ProgressManager::Enabled())
- ProgressManager::Instance().Decrement(m_progress_data);
}
void Progress::Increment(uint64_t amount,
@@ -109,128 +101,11 @@ void Progress::ReportProgress() {
return; // An overflow in the m_completed counter. Just ignore these events.
// Change the category bit if we're an internal or external progress.
- uint32_t progress_category_bit =
- m_progress_data.origin == Progress::Origin::eExternal
- ? lldb::eBroadcastBitExternalProgress
- : lldb::eBroadcastBitProgress;
-
- Debugger::ReportProgress(m_progress_data.progress_id, m_progress_data.title,
- m_details, completed, m_total,
- m_progress_data.debugger_id, progress_category_bit);
- m_prev_completed = completed;
-}
-
-ProgressManager::ProgressManager()
- : m_entries(), m_alarm(std::chrono::milliseconds(100)) {}
-
-ProgressManager::~ProgressManager() {}
-
-void ProgressManager::Initialize() {
- assert(!InstanceImpl() && "Already initialized.");
- InstanceImpl().emplace();
-}
-
-void ProgressManager::Terminate() {
- assert(InstanceImpl() && "Already terminated.");
- InstanceImpl().reset();
-}
-
-bool ProgressManager::Enabled() { return InstanceImpl().operator bool(); }
-
-ProgressManager &ProgressManager::Instance() {
- assert(InstanceImpl() && "ProgressManager must be initialized");
- return *InstanceImpl();
-}
-
-std::optional<ProgressManager> &ProgressManager::InstanceImpl() {
- static std::optional<ProgressManager> g_progress_manager;
- return g_progress_manager;
-}
-
-void ProgressManager::Increment(const Progress::ProgressData &progress_data) {
- std::lock_guard<std::mutex> lock(m_entries_mutex);
-
- llvm::StringRef key = progress_data.title;
- bool new_entry = !m_entries.contains(key);
- Entry &entry = m_entries[progress_data.title];
-
- if (new_entry) {
- // This is a new progress event. Report progress and store the progress
- // data.
- ReportProgress(progress_data, EventType::Begin);
- entry.data = progress_data;
- } else if (entry.refcount == 0) {
- // This is an existing entry that was scheduled to be deleted but a new one
- // came in before the timer expired.
- assert(entry.handle != Alarm::INVALID_HANDLE);
-
- if (!m_alarm.Cancel(entry.handle)) {
- // The timer expired before we had a chance to cancel it. We have to treat
- // this as an entirely new progress event.
- ReportProgress(progress_data, EventType::Begin);
- }
- // Clear the alarm handle.
- entry.handle = Alarm::INVALID_HANDLE;
- }
-
- // Regardless of how we got here, we need to bump the reference count.
- entry.refcount++;
-}
-
-void ProgressManager::Decrement(const Progress::ProgressData &progress_data) {
- std::lock_guard<std::mutex> lock(m_entries_mutex);
- llvm::StringRef key = progress_data.title;
-
- auto it = m_entries.find(key);
- if (it == m_entries.end())
- return;
-
- Entry &entry = it->second;
- entry.refcount--;
-
- if (entry.refcount == 0) {
- assert(entry.handle == Alarm::INVALID_HANDLE);
-
- // Copy the key to a std::string so we can pass it by value to the lambda.
- // The underlying StringRef will not exist by the time the callback is
- // called.
- std::string key_str = std::string(key);
-
- // Start a timer. If it expires before we see another progress event, it
- // will be reported.
- entry.handle = m_alarm.Create([=]() { Expire(key_str); });
- }
-}
-
-void ProgressManager::ReportProgress(
- const Progress::ProgressData &progress_data, EventType type) {
- // The category bit only keeps track of when progress report categories have
- // started and ended, so clear the details and reset other fields when
- // broadcasting to it since that bit doesn't need that information.
- const uint64_t completed =
- (type == EventType::Begin) ? 0 : Progress::kNonDeterministicTotal;
- const uint32_t progress_category_bit =
- progress_data.origin == Progress::Origin::eExternal
- ? lldb::eBroadcastBitExternalProgressCategory
- : lldb::eBroadcastBitProgressCategory;
- Debugger::ReportProgress(progress_data.progress_id, progress_data.title, "",
- completed, Progress::kNonDeterministicTotal,
- progress_data.debugger_id, progress_category_bit);
-}
+ uint32_t progress_category_bit = m_origin == Progress::Origin::eExternal
+ ? lldb::eBroadcastBitExternalProgress
+ : lldb::eBroadcastBitProgress;
-void ProgressManager::Expire(llvm::StringRef key) {
- std::lock_guard<std::mutex> lock(m_entries_mutex);
-
- // This shouldn't happen but be resilient anyway.
- if (!m_entries.contains(key))
- return;
-
- // A new event came in and the alarm fired before we had a chance to restart
- // it.
- if (m_entries[key].refcount != 0)
- return;
-
- // We're done with this entry.
- ReportProgress(m_entries[key].data, EventType::End);
- m_entries.erase(key);
+ Debugger::ReportProgress(m_progress_id, m_title, m_details, completed,
+ m_total, m_debugger_id, progress_category_bit);
+ m_prev_completed = completed;
}
diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt
index f4be151756b3b..9be0c06a516ba 100644
--- a/lldb/source/Host/CMakeLists.txt
+++ b/lldb/source/Host/CMakeLists.txt
@@ -13,7 +13,6 @@ macro(add_host_subdirectory group)
endmacro()
add_host_subdirectory(common
- common/Alarm.cpp
common/FileAction.cpp
common/FileCache.cpp
common/File.cpp
diff --git a/lldb/source/Host/common/Alarm.cpp b/lldb/source/Host/common/Alarm.cpp
deleted file mode 100644
index afc770d20d7b1..0000000000000
--- a/lldb/source/Host/common/Alarm.cpp
+++ /dev/null
@@ -1,222 +0,0 @@
-//===-- Alarm.cpp ---------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "lldb/Host/Alarm.h"
-#include "lldb/Host/ThreadLauncher.h"
-#include "lldb/Utility/LLDBLog.h"
-#include "lldb/Utility/Log.h"
-
-using namespace lldb;
-using namespace lldb_private;
-
-Alarm::Alarm(Duration timeout, bool run_callback_on_exit)
- : m_timeout(timeout), m_run_callbacks_on_exit(run_callback_on_exit) {
- StartAlarmThread();
-}
-
-Alarm::~Alarm() { StopAlarmThread(); }
-
-Alarm::Handle Alarm::Create(std::function<void()> callback) {
- // Gracefully deal with the unlikely event that the alarm thread failed to
- // launch.
- if (!AlarmThreadRunning())
- return INVALID_HANDLE;
-
- // Compute the next expiration before we take the lock. This ensures that
- // waiting on the lock doesn't eat into the timeout.
- const TimePoint expiration = GetNextExpiration();
-
- Handle handle = INVALID_HANDLE;
-
- {
- std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
-
- // Create a new unique entry and remember its handle.
- m_entries.emplace_back(callback, expiration);
- handle = m_entries.back().handle;
-
- // Tell the alarm thread we need to recompute the next alarm.
- m_recompute_next_alarm = true;
- }
-
- m_alarm_cv.notify_one();
- return handle;
-}
-
-bool Alarm::Restart(Handle handle) {
- // Gracefully deal with the unlikely event that the alarm thread failed to
- // launch.
- if (!AlarmThreadRunning())
- return false;
-
- // Compute the next expiration before we take the lock. This ensures that
- // waiting on the lock doesn't eat into the timeout.
- const TimePoint expiration = GetNextExpiration();
-
- {
- std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
-
- // Find the entry corresponding to the given handle.
- const auto it =
- std::find_if(m_entries.begin(), m_entries.end(),
- [handle](Entry &entry) { return entry.handle == handle; });
- if (it == m_entries.end())
- return false;
-
- // Update the expiration.
- it->expiration = expiration;
-
- // Tell the alarm thread we need to recompute the next alarm.
- m_recompute_next_alarm = true;
- }
-
- m_alarm_cv.notify_one();
- return true;
-}
-
-bool Alarm::Cancel(Handle handle) {
- // Gracefully deal with the unlikely event that the alarm thread failed to
- // launch.
- if (!AlarmThreadRunning())
- return false;
-
- {
- std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
-
- const auto it =
- std::find_if(m_entries.begin(), m_entries.end(),
- [handle](Entry &entry) { return entry.handle == handle; });
-
- if (it == m_entries.end())
- return false;
-
- m_entries.erase(it);
- }
-
- // No need to notify the alarm thread. This only affects the alarm thread if
- // we removed the entry that corresponds to the next alarm. If that's the
- // case, the thread will wake up as scheduled, find no expired events, and
- // recompute the next alarm time.
- return true;
-}
-
-Alarm::Entry::Entry(Alarm::Callback callback, Alarm::TimePoint expiration)
- : handle(Alarm::GetNextUniqueHandle()), callback(std::move(callback)),
- expiration(std::move(expiration)) {}
-
-void Alarm::StartAlarmThread() {
- if (!m_alarm_thread.IsJoinable()) {
- llvm::Expected<HostThread> alarm_thread = ThreadLauncher::LaunchThread(
- "lldb.debugger.alarm-thread", [this] { return AlarmThread(); },
- 8 * 1024 * 1024); // Use larger 8MB stack for this thread
- if (alarm_thread) {
- m_alarm_thread = *alarm_thread;
- } else {
- LLDB_LOG_ERROR(GetLog(LLDBLog::Host), alarm_thread.takeError(),
- "failed to launch host thread: {0}");
- }
- }
-}
-
-void Alarm::StopAlarmThread() {
- if (m_alarm_thread.IsJoinable()) {
- {
- std::lock_guard<std::mutex> alarm_guard(m_alarm_mutex);
- m_exit = true;
- }
- m_alarm_cv.notify_one();
- m_alarm_thread.Join(nullptr);
- }
-}
-
-bool Alarm::AlarmThreadRunning() { return m_alarm_thread.IsJoinable(); }
-
-lldb::thread_result_t Alarm::AlarmThread() {
- bool exit = false;
- std::optional<TimePoint> next_alarm;
-
- const auto predicate = [this] { return m_exit || m_recompute_next_alarm; };
-
- while (!exit) {
- // Synchronization between the main thread and the alarm thread using a
- // mutex and condition variable. There are 2 reasons the thread can wake up:
- //
- // 1. The timeout for the next alarm expired.
- //
- // 2. The condition variable is notified that one of our shared variables
- // (see predicate) was modified. Either the thread is asked to shut down
- // or a new alarm came in and we need to recompute the next timeout.
- //
- // Below we only deal with the timeout expiring and fall through for dealing
- // with the rest.
- llvm::SmallVector<Callback, 1> callbacks;
- {
- std::unique_lock<std::mutex> alarm_lock(m_alarm_mutex);
- if (next_alarm) {
- if (!m_alarm_cv.wait_until(alarm_lock, *next_alarm, predicate)) {
- // The timeout for the next alarm expired.
-
- // Clear the next timeout to signal that we need to recompute the next
- // timeout.
- next_alarm.reset();
-
- // Iterate over all the callbacks. Call the ones that have expired
- // and remove them from the list.
- const TimePoint now = std::chrono::system_clock::now();
- auto it = m_entries.begin();
- while (it != m_entries.end()) {
- if (it->expiration <= now) {
- callbacks.emplace_back(std::move(it->callback));
- it = m_entries.erase(it);
- } else {
- it++;
- }
- }
- }
- } else {
- m_alarm_cv.wait(alarm_lock, predicate);
- }
-
- // Fall through after waiting on the condition variable. At this point
- // either the predicate is true or we woke up because an alarm expired.
-
- // The alarm thread is shutting down.
- if (m_exit) {
- exit = true;
- if (m_run_callbacks_on_exit) {
- for (Entry &entry : m_entries)
- callbacks.emplace_back(std::move(entry.callback));
- }
- }
-
- // A new alarm was added or an alarm expired. Either way we need to
- // recompute when this thread should wake up for the next alarm.
- if (m_recompute_next_alarm || !next_alarm) {
- for (Entry &entry : m_entries) {
- if (!next_alarm || entry.expiration < *next_alarm)
- next_alarm = entry.expiration;
- }
- m_recompute_next_alarm = false;
- }
- }
-
- // Outside the lock, call the callbacks.
- for (Callback &callback : callbacks)
- callback();
- }
- return {};
-}
-
-Alarm::TimePoint Alarm::GetNextExpiration() const {
- return std::chrono::system_clock::now() + m_timeout;
-}
-
-Alarm::Handle Alarm::GetNextUniqueHandle() {
- static std::atomic<Handle> g_next_handle = 1;
- return g_next_handle++;
-}
diff --git a/lldb/test/API/python_api/sbprogress/TestSBProgress.py b/lldb/test/API/python_api/sbprogress/TestSBProgress.py
index 2a0689a52a185..b64c4b9c7206e 100644
--- a/lldb/test/API/python_api/sbprogress/TestSBProgress.py
+++ b/lldb/test/API/python_api/sbprogress/TestSBProgress.py
@@ -43,7 +43,7 @@ def test_progress_finalize_non_deterministic_progress(self):
progress = lldb.SBProgress("Test SBProgress", "Test finalize", self.dbg)
listener = lldb.SBListener("Test listener")
broadcaster = self.dbg.GetBroadcaster()
- broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgressCategory)
+ broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgress)
event = lldb.SBEvent()
progress.Finalize()
self.assertTrue(listener.WaitForEvent(5, event))
@@ -57,7 +57,7 @@ def test_progress_finalize_deterministic_progress(self):
progress = lldb.SBProgress("Test SBProgress", "Test finalize", 13, self.dbg)
listener = lldb.SBListener("Test listener")
broadcaster = self.dbg.GetBroadcaster()
- broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgressCategory)
+ broadcaster.AddListener(listener, lldb.eBroadcastBitExternalProgress)
event = lldb.SBEvent()
progress.Finalize()
self.assertTrue(listener.WaitForEvent(5, event))
diff --git a/lldb/unittests/Callback/TestBreakpointSetCallback.cpp b/lldb/unittests/Callback/TestBreakpointSetCallback.cpp
index 3dba4a9eb719e..c29cac5d2e5e7 100644
--- a/lldb/unittests/Callback/TestBreakpointSetCallback.cpp
+++ b/lldb/unittests/Callback/TestBreakpointSetCallback.cpp
@@ -52,8 +52,7 @@ class BreakpointSetCallbackTest : public ::testing::Test {
DebuggerSP m_debugger_sp;
PlatformSP m_platform_sp;
- SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX, ProgressManager>
- subsystems;
+ SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX> subsystems;
};
TEST_F(BreakpointSetCallbackTest, TestBreakpointSetCallback) {
diff --git a/lldb/unittests/Core/ProgressReportTest.cpp b/lldb/unittests/Core/ProgressReportTest.cpp
index 0943d7b990809..592e13a79e8d7 100644
--- a/lldb/unittests/Core/ProgressReportTest.cpp
+++ b/lldb/unittests/Core/ProgressReportTest.cpp
@@ -57,8 +57,7 @@ class ProgressReportTest : public ::testing::Test {
DebuggerSP m_debugger_sp;
ListenerSP m_listener_sp;
- SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX, ProgressManager>
- subsystems;
+ SubsystemRAII<FileSystem, HostInfo, PlatformMacOSX> subsystems;
};
TEST_F(ProgressReportTest, TestReportCreation) {
@@ -313,119 +312,6 @@ TEST_F(ProgressReportTest, TestMinimumReportTime) {
ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
}
-TEST_F(ProgressReportTest, TestProgressManager) {
- ListenerSP listener_sp =
- CreateListenerFor(lldb::eBroadcastBitProgressCategory);
- EventSP event_sp;
- const ProgressEventData *data;
-
- // Create three progress events with the same category then try to pop 2
- // events from the queue in a row before the progress reports are destroyed.
- // Since only 1 event should've been broadcast for this category, the second
- // GetEvent() call should return false.
- {
- Progress progress1("Progress report 1", "Starting report 1");
- Progress progress2("Progress report 1", "Starting report 2");
- Progress progress3("Progress report 1", "Starting report 3");
- ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
- ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
- }
-
- data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
-
- EXPECT_EQ(data->GetDetails(), "");
- EXPECT_FALSE(data->IsFinite());
- EXPECT_FALSE(data->GetCompleted());
- EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
- EXPECT_EQ(data->GetMessage(), "Progress report 1");
-
- // Pop another event from the queue, this should be the event for the final
- // report for this category.
- ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
- data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
-
- EXPECT_EQ(data->GetDetails(), "");
- EXPECT_FALSE(data->IsFinite());
- EXPECT_TRUE(data->GetCompleted());
- EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
- EXPECT_EQ(data->GetMessage(), "Progress report 1");
-}
-
-TEST_F(ProgressReportTest, TestOverlappingEvents) {
- ListenerSP listener_sp =
- CreateListenerFor(lldb::eBroadcastBitProgressCategory);
- EventSP event_sp;
- const ProgressEventData *data;
-
- // Create two progress reports of the same category that overlap with each
- // other. Here we want to ensure that the ID broadcasted for the initial and
- // final reports for this category are the same.
- std::unique_ptr<Progress> overlap_progress1 =
- std::make_unique<Progress>("Overlapping report 1", "Starting report 1");
- std::unique_ptr<Progress> overlap_progress2 =
- std::make_unique<Progress>("Overlapping report 1", "Starting report 2");
- overlap_progress1.reset();
-
- ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
- data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
- // Get the ID used in the first report for this category.
- uint64_t expected_progress_id = data->GetID();
-
- EXPECT_EQ(data->GetDetails(), "");
- EXPECT_FALSE(data->IsFinite());
- EXPECT_FALSE(data->GetCompleted());
- EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
- EXPECT_EQ(data->GetMessage(), "Overlapping report 1");
-
- overlap_progress2.reset();
-
- ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
- data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
-
- EXPECT_EQ(data->GetDetails(), "");
- EXPECT_FALSE(data->IsFinite());
- EXPECT_TRUE(data->GetCompleted());
- EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
- EXPECT_EQ(data->GetMessage(), "Overlapping report 1");
- // The progress ID for the final report should be the same as that for the
- // initial report.
- EXPECT_EQ(data->GetID(), expected_progress_id);
-}
-
-TEST_F(ProgressReportTest, TestProgressManagerDisjointReports) {
- ListenerSP listener_sp =
- CreateListenerFor(lldb::eBroadcastBitProgressCategory);
- EventSP event_sp;
- const ProgressEventData *data;
- uint64_t expected_progress_id;
-
- { Progress progress("Coalesced report 1", "Starting report 1"); }
- { Progress progress("Coalesced report 1", "Starting report 2"); }
- { Progress progress("Coalesced report 1", "Starting report 3"); }
-
- ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
- data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
- expected_progress_id = data->GetID();
-
- EXPECT_EQ(data->GetDetails(), "");
- EXPECT_FALSE(data->IsFinite());
- EXPECT_FALSE(data->GetCompleted());
- EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
- EXPECT_EQ(data->GetMessage(), "Coalesced report 1");
-
- ASSERT_TRUE(listener_sp->GetEvent(event_sp, TIMEOUT));
- data = ProgressEventData::GetEventDataFromEvent(event_sp.get());
-
- EXPECT_EQ(data->GetID(), expected_progress_id);
- EXPECT_EQ(data->GetDetails(), "");
- EXPECT_FALSE(data->IsFinite());
- EXPECT_TRUE(data->GetCompleted());
- EXPECT_EQ(data->GetTotal(), Progress::kNonDeterministicTotal);
- EXPECT_EQ(data->GetMessage(), "Coalesced report 1");
-
- ASSERT_FALSE(listener_sp->GetEvent(event_sp, TIMEOUT));
-}
-
TEST_F(ProgressReportTest, TestExternalReportCreation) {
ListenerSP listener_sp =
CreateListenerFor(lldb::eBroadcastBitExternalProgress);
diff --git a/lldb/unittests/Host/AlarmTest.cpp b/lldb/unittests/Host/AlarmTest.cpp
deleted file mode 100644
index 94e72af3ffe8d..0000000000000
--- a/lldb/unittests/Host/AlarmTest.cpp
+++ /dev/null
@@ -1,172 +0,0 @@
-//===-- AlarmTest.cpp -----------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "lldb/Host/Alarm.h"
-#include "gtest/gtest.h"
-
-#include <chrono>
-#include <thread>
-
-using namespace lldb_private;
-using namespace std::chrono_literals;
-
-// Increase the timeout tenfold when running under ASan as it can have about the
-// same performance overhead.
-#if __has_feature(address_sanitizer)
-static constexpr auto TEST_TIMEOUT = 10000ms;
-#else
-static constexpr auto TEST_TIMEOUT = 1000ms;
-#endif
-
-// The time between scheduling a callback and it getting executed. This should
-// NOT be increased under ASan.
-static constexpr auto ALARM_TIMEOUT = 500ms;
-
-// If there are any pending callbacks, make sure they run before the Alarm
-// object is destroyed.
-static constexpr bool RUN_CALLBACKS_ON_EXIT = true;
-
-TEST(AlarmTest, Create) {
- std::mutex m;
-
- std::vector<Alarm::TimePoint> callbacks_actual;
- std::vector<Alarm::TimePoint> callbacks_expected;
-
- Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
-
- // Create 5 alarms some time apart.
- for (size_t i = 0; i < 5; ++i) {
- callbacks_actual.emplace_back();
- callbacks_expected.emplace_back(std::chrono::system_clock::now() +
- ALARM_TIMEOUT);
-
- alarm.Create([&callbacks_actual, &m, i]() {
- std::lock_guard<std::mutex> guard(m);
- callbacks_actual[i] = std::chrono::system_clock::now();
- });
-
- std::this_thread::sleep_for(ALARM_TIMEOUT / 5);
- }
-
- // Leave plenty of time for all the alarms to fire.
- std::this_thread::sleep_for(TEST_TIMEOUT);
-
- // Acquire the lock to check the callbacks.
- std::lock_guard<std::mutex> guard(m);
-
- // Make sure all the alarms fired around the expected time.
- for (size_t i = 0; i < 5; ++i)
- EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
-}
-
-TEST(AlarmTest, Exit) {
- std::mutex m;
-
- std::vector<Alarm::Handle> handles;
- std::vector<bool> callbacks;
-
- {
- Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
-
- // Create 5 alarms.
- for (size_t i = 0; i < 5; ++i) {
- callbacks.emplace_back(false);
-
- handles.push_back(alarm.Create([&callbacks, &m, i]() {
- std::lock_guard<std::mutex> guard(m);
- callbacks[i] = true;
- }));
- }
-
- // Let the alarm go out of scope before any alarm had a chance to fire.
- }
-
- // Acquire the lock to check the callbacks.
- std::lock_guard<std::mutex> guard(m);
-
- // Make sure none of the alarms fired.
- for (bool callback : callbacks)
- EXPECT_TRUE(callback);
-}
-
-TEST(AlarmTest, Cancel) {
- std::mutex m;
-
- std::vector<Alarm::Handle> handles;
- std::vector<bool> callbacks;
-
- Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
-
- // Create 5 alarms.
- for (size_t i = 0; i < 5; ++i) {
- callbacks.emplace_back(false);
-
- handles.push_back(alarm.Create([&callbacks, &m, i]() {
- std::lock_guard<std::mutex> guard(m);
- callbacks[i] = true;
- }));
- }
-
- // Make sure we can cancel the first 4 alarms.
- for (size_t i = 0; i < 4; ++i)
- EXPECT_TRUE(alarm.Cancel(handles[i]));
-
- // Leave plenty of time for all the alarms to fire.
- std::this_thread::sleep_for(TEST_TIMEOUT);
-
- // Acquire the lock to check the callbacks.
- std::lock_guard<std::mutex> guard(m);
-
- // Make sure none of the first 4 alarms fired.
- for (size_t i = 0; i < 4; ++i)
- EXPECT_FALSE(callbacks[i]);
-
- // Make sure the fifth alarm still fired.
- EXPECT_TRUE(callbacks[4]);
-}
-
-TEST(AlarmTest, Restart) {
- std::mutex m;
-
- std::vector<Alarm::Handle> handles;
- std::vector<Alarm::TimePoint> callbacks_actual;
- std::vector<Alarm::TimePoint> callbacks_expected;
-
- Alarm alarm(ALARM_TIMEOUT, RUN_CALLBACKS_ON_EXIT);
-
- // Create 5 alarms some time apart.
- for (size_t i = 0; i < 5; ++i) {
- callbacks_actual.emplace_back();
- callbacks_expected.emplace_back(std::chrono::system_clock::now() +
- ALARM_TIMEOUT);
-
- handles.push_back(alarm.Create([&callbacks_actual, &m, i]() {
- std::lock_guard<std::mutex> guard(m);
- callbacks_actual[i] = std::chrono::system_clock::now();
- }));
-
- std::this_thread::sleep_for(ALARM_TIMEOUT / 5);
- }
-
- // Update the last 2 alarms.
- for (size_t i = 3; i < 5; ++i) {
- std::lock_guard<std::mutex> guard(m);
- callbacks_expected[i] = std::chrono::system_clock::now() + ALARM_TIMEOUT;
- EXPECT_TRUE(alarm.Restart(handles[i]));
- }
-
- // Leave plenty of time for all the alarms to fire.
- std::this_thread::sleep_for(TEST_TIMEOUT);
-
- // Acquire the lock to check the callbacks.
- std::lock_guard<std::mutex> guard(m);
-
- // Make sure all the alarms around the expected time.
- for (size_t i = 0; i < 5; ++i)
- EXPECT_GE(callbacks_actual[i], callbacks_expected[i]);
-}
diff --git a/lldb/unittests/Host/CMakeLists.txt b/lldb/unittests/Host/CMakeLists.txt
index 7c09932b39c2a..c959478970d18 100644
--- a/lldb/unittests/Host/CMakeLists.txt
+++ b/lldb/unittests/Host/CMakeLists.txt
@@ -1,5 +1,4 @@
set (FILES
- AlarmTest.cpp
ConnectionFileDescriptorTest.cpp
FileActionTest.cpp
FileSystemTest.cpp
More information about the lldb-commits
mailing list