diff --git a/lib/base/CMakeLists.txt b/lib/base/CMakeLists.txt index 6f2a6d95c..bf50ee123 100644 --- a/lib/base/CMakeLists.txt +++ b/lib/base/CMakeLists.txt @@ -16,7 +16,7 @@ set(base_SOURCES i2-base.hpp application.cpp application.hpp application-ti.hpp application-version.cpp application-environment.cpp array.cpp array.hpp array-script.cpp - atomic.hpp + atomic.cpp atomic.hpp atomic-file.cpp atomic-file.hpp base64.cpp base64.hpp boolean.cpp boolean.hpp boolean-script.cpp diff --git a/lib/base/atomic.cpp b/lib/base/atomic.cpp new file mode 100644 index 000000000..3066e3642 --- /dev/null +++ b/lib/base/atomic.cpp @@ -0,0 +1,20 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#include "base/atomic.hpp" + +using namespace icinga; + +/** + * Adds the elapsedTime to this instance. + * + * May be called multiple times to accumulate time. + * + * @param elapsedTime The distance between two time points + * + * @return This instance for method chaining + */ +AtomicDuration& AtomicDuration::operator+=(const Clock::duration& elapsedTime) noexcept +{ + m_Sum.fetch_add(elapsedTime.count(), std::memory_order_relaxed); + return *this; +} diff --git a/lib/base/atomic.hpp b/lib/base/atomic.hpp index 855850336..f0546fd04 100644 --- a/lib/base/atomic.hpp +++ b/lib/base/atomic.hpp @@ -4,6 +4,7 @@ #define ATOMIC_H #include +#include #include #include #include @@ -34,6 +35,31 @@ public: } }; +/** + * Accumulates time durations atomically. + * + * @ingroup base + */ +class AtomicDuration +{ +public: + using Clock = std::chrono::steady_clock; + + AtomicDuration& operator+=(const Clock::duration&) noexcept; + + /** + * @return The total accumulated time in seconds + */ + template + operator T() const noexcept + { + return std::chrono::duration(Clock::duration(m_Sum.load(std::memory_order_relaxed))).count(); + } + +private: + Atomic m_Sum {0}; +}; + /** * Wraps any T into a std::atomic-like interface that locks using a mutex. * diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c4b1041dd..1fbad98a9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -58,6 +58,7 @@ add_boost_test(types set(base_test_SOURCES icingaapplication-fixture.cpp base-array.cpp + base-atomic.cpp base-base64.cpp base-convert.cpp base-dictionary.cpp @@ -112,6 +113,9 @@ add_boost_test(base base_array/foreach base_array/clone base_array/json + base_atomic/duration_none + base_atomic/duration_one + base_atomic/duration_two base_base64/base64 base_convert/tolong base_convert/todouble diff --git a/test/base-atomic.cpp b/test/base-atomic.cpp new file mode 100644 index 000000000..1888fecc1 --- /dev/null +++ b/test/base-atomic.cpp @@ -0,0 +1,34 @@ +/* Icinga 2 | (c) 2025 Icinga GmbH | GPLv2+ */ + +#include "base/atomic.hpp" +#include + +using namespace icinga; + +BOOST_AUTO_TEST_SUITE(base_atomic) + +BOOST_AUTO_TEST_CASE(duration_none) +{ + BOOST_CHECK_EQUAL((double)AtomicDuration(), 0); +} + +BOOST_AUTO_TEST_CASE(duration_one) +{ + AtomicDuration sum; + + sum += std::chrono::seconds(1); + + BOOST_CHECK_EQUAL((double)sum, 1); +} + +BOOST_AUTO_TEST_CASE(duration_two) +{ + AtomicDuration sum; + + sum += std::chrono::seconds(1); + sum += std::chrono::seconds(2); + + BOOST_CHECK_EQUAL((double)sum, 3); +} + +BOOST_AUTO_TEST_SUITE_END()