diff --git a/include/xrpl/basics/CountedObject.h b/include/xrpl/basics/CountedObject.h index 9ea76aa3bd..28d918b5fc 100644 --- a/include/xrpl/basics/CountedObject.h +++ b/include/xrpl/basics/CountedObject.h @@ -1,75 +1,31 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - #ifndef RIPPLE_BASICS_COUNTEDOBJECT_H_INCLUDED #define RIPPLE_BASICS_COUNTEDOBJECT_H_INCLUDED #include #include +#include +#include #include -#include -#include namespace ripple { -/** Manages all counted object types. */ class CountedObjects { public: - static CountedObjects& - getInstance() noexcept; - - using Entry = std::pair; - using List = std::vector; - - List - getCounts(int minimumThreshold) const; - -public: - /** Implementation for @ref CountedObject. - - @internal - */ class Counter { public: - Counter(std::string name) noexcept : name_(std::move(name)), count_(0) - { - // Insert ourselves at the front of the lock-free linked list - CountedObjects& instance = CountedObjects::getInstance(); - Counter* head; - - do - { - head = instance.m_head.load(); - next_ = head; - } while (instance.m_head.exchange(this) != head); - - ++instance.m_count; - } - - ~Counter() noexcept = default; + Counter(std::string name) noexcept; int increment() noexcept { - return ++count_; + auto const newCount = ++count_; + + if (auto maxCount = maxCount_.load(); newCount > maxCount) + maxCount_.compare_exchange_strong(maxCount, newCount); + + return newCount; } int @@ -79,78 +35,136 @@ public: } int - getCount() const noexcept + count() const noexcept { return count_.load(); } - Counter* - getNext() const noexcept + int + max() const noexcept { - return next_; + return std::max(count_.load(), maxCount_.load()); } std::string const& - getName() const noexcept + name() const noexcept { return name_; } private: - std::string const name_; - std::atomic count_; + friend class CountedObjects; + Counter* next_; + std::atomic count_ = 0; + std::atomic maxCount_ = 0; + std::string const name_; }; -private: - CountedObjects() noexcept; - ~CountedObjects() noexcept = default; + class Iterator + { + public: + using value_type = Counter const; + using reference = value_type&; + using pointer = value_type*; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + explicit Iterator(Counter* c = nullptr) noexcept : current_(c) + { + } + + reference + operator*() const noexcept + { + return *current_; + } + pointer + operator->() const noexcept + { + return current_; + } + + Iterator& + operator++() noexcept + { + current_ = current_->next_; + return *this; + } + + Iterator + operator++(int) noexcept + { + auto tmp = *this; + ++*this; + return tmp; + } + + bool + operator==(Iterator const&) const noexcept = default; + + private: + Counter* current_; + }; + + constexpr CountedObjects() noexcept = default; + + auto + begin() const noexcept + { + return Iterator{head_.load()}; + } + auto + end() const noexcept + { + return Iterator{}; + } private: - std::atomic m_count; - std::atomic m_head; + friend class Counter; + + std::atomic head_ = nullptr; }; +inline constinit CountedObjects countedObjects; + +inline CountedObjects::Counter::Counter(std::string name) noexcept + : name_(std::move(name)) +{ + do + next_ = countedObjects.head_.load(); + while (!countedObjects.head_.compare_exchange_weak(next_, this)); +} + //------------------------------------------------------------------------------ -/** Tracks the number of instances of an object. - - Derived classes have their instances counted automatically. This is used - for reporting purposes. - - @ingroup ripple_basics -*/ template class CountedObject { -private: - static auto& - getCounter() noexcept - { - static CountedObjects::Counter c{beast::type_name()}; - return c; - } + static CountedObjects::Counter counter_; public: CountedObject() noexcept { - getCounter().increment(); + counter_.increment(); } - CountedObject(CountedObject const&) noexcept { - getCounter().increment(); + counter_.increment(); } - CountedObject& operator=(CountedObject const&) noexcept = default; - ~CountedObject() noexcept { - getCounter().decrement(); + counter_.decrement(); } }; +// Instantiation of the static CountedObject::counter_ +template +CountedObjects::Counter CountedObject::counter_{ + beast::type_name()}; + } // namespace ripple #endif diff --git a/include/xrpl/protocol/jss.h b/include/xrpl/protocol/jss.h index 964457536c..cae5cf9707 100644 --- a/include/xrpl/protocol/jss.h +++ b/include/xrpl/protocol/jss.h @@ -464,6 +464,7 @@ JSS(max_ledger); // in/out: LedgerCleaner JSS(max_queue_size); // out: TxQ JSS(max_spend_drops); // out: AccountInfo JSS(max_spend_drops_total); // out: AccountInfo +JSS(maximum); JSS(mean); // out: get_aggregate_price JSS(median); // out: get_aggregate_price JSS(median_fee); // out: TxQ diff --git a/src/libxrpl/basics/CountedObject.cpp b/src/libxrpl/basics/CountedObject.cpp deleted file mode 100644 index c18152938a..0000000000 --- a/src/libxrpl/basics/CountedObject.cpp +++ /dev/null @@ -1,58 +0,0 @@ -//------------------------------------------------------------------------------ -/* - This file is part of rippled: https://github.com/ripple/rippled - Copyright (c) 2012, 2013 Ripple Labs Inc. - - Permission to use, copy, modify, and/or distribute this software for any - purpose with or without fee is hereby granted, provided that the above - copyright notice and this permission notice appear in all copies. - - THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -*/ -//============================================================================== - -#include -#include -#include - -namespace ripple { - -CountedObjects& -CountedObjects::getInstance() noexcept -{ - static CountedObjects instance; - - return instance; -} - -CountedObjects::CountedObjects() noexcept : m_count(0), m_head(nullptr) -{ -} - -CountedObjects::List -CountedObjects::getCounts(int minimumThreshold) const -{ - List counts; - - // When other operations are concurrent, the count - // might be temporarily less than the actual count. - counts.reserve(m_count.load()); - - for (auto* ctr = m_head.load(); ctr != nullptr; ctr = ctr->getNext()) - { - if (ctr->getCount() >= minimumThreshold) - counts.emplace_back(ctr->getName(), ctr->getCount()); - } - - std::sort(counts.begin(), counts.end()); - - return counts; -} - -} // namespace ripple diff --git a/src/test/app/Regression_test.cpp b/src/test/app/Regression_test.cpp index 7e771e6d1d..5462742576 100644 --- a/src/test/app/Regression_test.cpp +++ b/src/test/app/Regression_test.cpp @@ -293,11 +293,11 @@ struct Regression_test : public beast::unit_test::suite return digest.as_uint256(); }(); - auto const mapCounts = [&](CountedObjects::List const& list) { + auto const mapCounts = [&]() { std::map result; - for (auto const& e : list) + for (auto const& e : countedObjects) { - result[e.first] = e.second; + result[e.name()] = e.count(); } return result; @@ -308,14 +308,12 @@ struct Regression_test : public beast::unit_test::suite { auto& cache = env.app().cachedSLEs(); cache.del(*digest, false); - auto const beforeCounts = - mapCounts(CountedObjects::getInstance().getCounts(0)); + auto const beforeCounts = mapCounts(); env(check::cash(alice, bob_index, check::DeliverMin(XRP(100))), ter(tecNO_ENTRY)); - auto const afterCounts = - mapCounts(CountedObjects::getInstance().getCounts(0)); + auto const afterCounts = mapCounts(); using namespace std::string_literals; BEAST_EXPECT( diff --git a/src/test/rpc/GetCounts_test.cpp b/src/test/rpc/GetCounts_test.cpp index 7411dddc80..2f55909108 100644 --- a/src/test/rpc/GetCounts_test.cpp +++ b/src/test/rpc/GetCounts_test.cpp @@ -61,17 +61,26 @@ class GetCounts_test : public beast::unit_test::suite env.close(); } + auto getCountedObjects = [](int minimumCount) { + std::vector> result; + + for (auto const& c : countedObjects) + if (c.count() >= minimumCount) + result.emplace_back(c.name(), c.count()); + + return result; + }; + { // check counts, default params result = env.rpc("get_counts")[jss::result]; BEAST_EXPECT(result[jss::status] == "success"); - // compare with values reported by CountedObjects - auto const& objectCounts = - CountedObjects::getInstance().getCounts(10); - for (auto const& it : objectCounts) + for (auto const& it : getCountedObjects(10)) { BEAST_EXPECTS(result.isMember(it.first), it.first); - BEAST_EXPECTS(result[it.first].asInt() == it.second, it.first); + BEAST_EXPECTS( + result[it.first][jss::current].asInt() == it.second, + it.first); } BEAST_EXPECT(!result.isMember(jss::local_txs)); } @@ -81,14 +90,12 @@ class GetCounts_test : public beast::unit_test::suite // that only STObject and NodeObject are reported result = env.rpc("get_counts", "100")[jss::result]; BEAST_EXPECT(result[jss::status] == "success"); - - // compare with values reported by CountedObjects - auto const& objectCounts = - CountedObjects::getInstance().getCounts(100); - for (auto const& it : objectCounts) + for (auto const& it : getCountedObjects(100)) { BEAST_EXPECTS(result.isMember(it.first), it.first); - BEAST_EXPECTS(result[it.first].asInt() == it.second, it.first); + BEAST_EXPECTS( + result[it.first][jss::current].asInt() == it.second, + it.first); } BEAST_EXPECT(!result.isMember("Transaction")); BEAST_EXPECT(!result.isMember("STTx")); diff --git a/src/xrpld/app/misc/DatagramMonitor.h b/src/xrpld/app/misc/DatagramMonitor.h index 74b270964d..fb801e310d 100644 --- a/src/xrpld/app/misc/DatagramMonitor.h +++ b/src/xrpld/app/misc/DatagramMonitor.h @@ -408,8 +408,6 @@ private: getDebugCounters() { DebugCounters counters; - ObjectCountMap objectCounts = - CountedObjects::getInstance().getCounts(1); // Database metrics if applicable if (app_.config().useTxTables()) @@ -456,7 +454,15 @@ private: counters.nodeFetchHitCount = app_.getNodeStore().getFetchHitCount(); counters.nodeFetchSize = app_.getNodeStore().getFetchSize(); - return {counters, objectCounts}; + return {counters, []() { + std::vector> result; + + for (auto const& c : countedObjects) + if (c.count()) + result.emplace_back(c.name(), c.count()); + + return result; + }()}; } uint32_t diff --git a/src/xrpld/rpc/handlers/GetCounts.cpp b/src/xrpld/rpc/handlers/GetCounts.cpp index 690106ebbd..cf25d44395 100644 --- a/src/xrpld/rpc/handlers/GetCounts.cpp +++ b/src/xrpld/rpc/handlers/GetCounts.cpp @@ -62,13 +62,17 @@ textTime( Json::Value getCountsJson(Application& app, int minObjectCount) { - auto objectCounts = CountedObjects::getInstance().getCounts(minObjectCount); - Json::Value ret(Json::objectValue); - for (auto const& [k, v] : objectCounts) + for (auto const& c : countedObjects) { - ret[k] = v; + if (c.count() >= minObjectCount) + { + Json::Value obj(Json::objectValue); + obj[jss::current] = c.count(); + obj[jss::maximum] = c.max(); + ret[c.name()] = std::move(obj); + } } if (app.config().useTxTables())