diff --git a/include/stdex/progress.hpp b/include/stdex/progress.hpp index c45d5362f..4ec3e2fff 100644 --- a/include/stdex/progress.hpp +++ b/include/stdex/progress.hpp @@ -7,7 +7,10 @@ #include "compat.hpp" #include "interval.hpp" +#include #include +#include +#include namespace stdex { @@ -69,7 +72,7 @@ namespace stdex }; /// - /// Lazy progress indicator base class + /// Lazy progress indicator /// /// Use with expensive progress reporting to suppress progress indication for a period of time. /// @@ -135,7 +138,7 @@ namespace stdex }; /// - /// Global progress indicator base class + /// Global progress indicator /// /// Use to report progress of a phase or section as a part of a whole progress. /// @@ -148,7 +151,7 @@ namespace stdex /// /// \param[in] host Host progress indicator /// - global_progress(_In_opt_ progress* host = NULL) : m_host(host) + global_progress(_In_opt_ progress* host = nullptr) : m_host(host) {} /// @@ -169,7 +172,7 @@ namespace stdex progress* detach() { progress* k = m_host; - m_host = NULL; + m_host = nullptr; return k; } @@ -267,7 +270,6 @@ namespace stdex /// /// Use to inject global_progress indicator inplace of another progress indicator. /// - template class progress_switcher : public global_progress { @@ -287,4 +289,162 @@ namespace stdex protected: progress*& m_host_ref; }; + + /// + /// Aggregated progress indicator + /// + /// Use to report combined progress from multiple workers. + /// + template + class aggregate_progress + { + protected: + /// + /// Progress indicator for individual worker + /// + class worker_progress : public progress + { + protected: + aggregate_progress& m_host; + T m_start, m_end, m_value; + + public: + worker_progress(_Inout_ aggregate_progress& host) : + m_host(host), + m_start(0), m_end(0), + m_value(0) + {} + + /// + /// Set progress indicator text + /// + /// \param[in] msg Text to display + /// + virtual void set_text(_In_ const char* msg) + { + std::shared_lock lk(m_host.m_mutex); + if (m_host.m_host) + m_host.m_host->set_text(msg); + } + + /// + /// Set local extend of the progress indicator + /// + /// \param[in] start Minimum value of the progress + /// \param[in] end Maximum value of the progress + /// + virtual void set_range(_In_ T start, _In_ T end) + { + T + combined_start = m_host.m_start += start - m_start, + combined_end = m_host.m_end += end - m_end; + m_start = start; + m_end = end; + std::shared_lock lk(m_host.m_mutex); + if (m_host.m_host) + m_host.m_host->set_range(combined_start, combined_end); + } + + /// + /// Set local current progress + /// + /// \param[in] value Current value of the progress. Must be between start and end parameters provided in set_range() call. + /// + virtual void set(_In_ T value) + { + T combined_value = m_host.m_value += value - m_value; + m_value = value; + std::shared_lock lk(m_host.m_mutex); + if (m_host.m_host) + m_host.m_host->set(combined_value); + } + + /// + /// Show or hide progress + /// + /// \param[in] show Shows or hides progress indicator + /// + virtual void show(_In_ bool show = true) + { + std::shared_lock lk(m_host.m_mutex); + if (m_host.m_host) + m_host.m_host->show(show); + } + + /// + /// Query whether user requested abort + /// + virtual bool cancel() + { + std::shared_lock lk(m_host.m_mutex); + return m_host.m_host && m_host.m_host->cancel(); + } + }; + + progress* m_host; + std::atomic m_start, m_end, m_value; + std::vector m_workers; + std::shared_mutex m_mutex; + + public: + /// + /// Constructs a progress indicator + /// + /// \param[in] num_workers Total number of workers + /// \param[in] host Host progress indicator + /// + aggregate_progress(_In_ size_t num_workers, _In_opt_ progress* host = nullptr) : + m_host(host), + m_start(0), m_end(0), + m_value(0) + { + m_workers.reserve(num_workers); + for (size_t i = 0; i < num_workers; ++i) + m_workers.push_back(std::move(worker_progress(*this))); + if (m_host) { + m_host->set_range(m_start, m_end); + m_host->set(m_value); + } + } + + /// + /// Attach to a host progress indicator + /// + /// \param[in] host Host progress indicator + /// + void attach(_In_opt_ progress* host) + { + std::unique_lock lk(m_mutex); + m_host = host; + if (m_host) { + m_host->set_range(m_start, m_end); + m_host->set(m_value); + } + } + + /// + /// Detach host progress indicator + /// + /// \returns Old host progress indicator + /// + progress* detach() + { + std::unique_lock lk(m_mutex); + progress* k = m_host; + m_host = nullptr; + return k; + } + + /// + /// Returns progress indicator for specific worker + /// + /// \param[in] index Index of worker + /// + /// \returns Reference to specific worker progress indicator + /// + progress& operator[](_In_ size_t index) + { + return m_workers[index]; + } + }; }