Merge branch 'develop' into mvadari/fix-bad-cast

This commit is contained in:
Mayukha Vadari
2025-12-23 11:52:15 -08:00
committed by GitHub
1451 changed files with 5935 additions and 74895 deletions

View File

@@ -3,7 +3,7 @@
#include <boost/filesystem.hpp>
namespace ripple {
namespace xrpl {
/** Extract a tar archive compressed with lz4
@@ -17,6 +17,6 @@ extractTarLz4(
boost::filesystem::path const& src,
boost::filesystem::path const& dst);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -12,7 +12,7 @@
#include <unordered_map>
#include <vector>
namespace ripple {
namespace xrpl {
using IniFileSections =
std::unordered_map<std::string, std::vector<std::string>>;
@@ -380,6 +380,6 @@ get_if_exists<bool>(Section const& section, std::string const& name, bool& v)
return stat;
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,13 +3,13 @@
#include <vector>
namespace ripple {
namespace xrpl {
/** Storage for linear binary data.
Blocks of binary data appear often in various idioms and structures.
*/
using Blob = std::vector<unsigned char>;
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -8,7 +8,7 @@
#include <cstring>
#include <memory>
namespace ripple {
namespace xrpl {
/** Like std::vector<char> but better.
Meets the requirements of BufferFactory.
@@ -96,7 +96,7 @@ public:
XRPL_ASSERT(
s.size() == 0 || size_ == 0 || s.data() < p_.get() ||
s.data() >= p_.get() + size_,
"ripple::Buffer::operator=(Slice) : input not a subset");
"xrpl::Buffer::operator=(Slice) : input not a subset");
if (auto p = alloc(s.size()))
std::memcpy(p, s.data(), s.size());
@@ -215,6 +215,6 @@ operator!=(Buffer const& lhs, Buffer const& rhs) noexcept
return !(lhs == rhs);
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -1,7 +1,7 @@
#ifndef XRPL_BASICS_BYTEUTILITIES_H_INCLUDED
#define XRPL_BASICS_BYTEUTILITIES_H_INCLUDED
namespace ripple {
namespace xrpl {
template <class T>
constexpr auto
@@ -19,6 +19,6 @@ megabytes(T value) noexcept
static_assert(kilobytes(2) == 2048, "kilobytes(2) == 2048");
static_assert(megabytes(3) == 3145728, "megabytes(3) == 3145728");
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -10,7 +10,7 @@
#include <stdexcept>
#include <vector>
namespace ripple {
namespace xrpl {
namespace compression_algorithms {
@@ -144,6 +144,6 @@ lz4Decompress(
} // namespace compression_algorithms
} // namespace ripple
} // namespace xrpl
#endif // XRPL_COMPRESSIONALGORITHMS_H_INCLUDED

View File

@@ -8,7 +8,7 @@
#include <utility>
#include <vector>
namespace ripple {
namespace xrpl {
/** Manages all counted object types. */
class CountedObjects
@@ -133,6 +133,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <chrono>
#include <cmath>
namespace ripple {
namespace xrpl {
/** Sampling function using exponential decay to provide a continuous value.
@tparam The number of seconds in the decay window.
@@ -131,6 +131,6 @@ private:
time_point when_;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -7,7 +7,7 @@
#include <stdexcept>
namespace ripple {
namespace xrpl {
/** Expected is an approximation of std::expected (hoped for in C++23)
@@ -232,6 +232,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_EXPECTED_H_INCLUDED

View File

@@ -6,7 +6,7 @@
#include <optional>
namespace ripple {
namespace xrpl {
std::string
getFileContents(
@@ -20,6 +20,6 @@ writeFileContents(
boost::filesystem::path const& destPath,
std::string const& contents);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <type_traits>
#include <utility>
namespace ripple {
namespace xrpl {
//------------------------------------------------------------------------------
@@ -492,5 +492,5 @@ dynamic_pointer_cast(TT const& v)
return SharedPtr<T>(DynamicCastTagSharedIntrusive{}, v);
}
} // namespace intr_ptr
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <utility>
namespace ripple {
namespace xrpl {
template <class T>
template <CAdoptTag TAdoptTag>
@@ -608,7 +608,7 @@ SharedWeakUnion<T>::convertToStrong()
[[maybe_unused]] auto action = p->releaseWeakRef();
XRPL_ASSERT(
(action == ReleaseWeakRefAction::noop),
"ripple::SharedWeakUnion::convertToStrong : "
"xrpl::SharedWeakUnion::convertToStrong : "
"action is noop");
unsafeSetRawPtr(p, RefStrength::strong);
return true;
@@ -637,7 +637,7 @@ SharedWeakUnion<T>::convertToWeak()
// We just added a weak ref. How could we destroy?
// LCOV_EXCL_START
UNREACHABLE(
"ripple::SharedWeakUnion::convertToWeak : destroying freshly "
"xrpl::SharedWeakUnion::convertToWeak : destroying freshly "
"added ref");
delete p;
unsafeSetRawPtr(nullptr);
@@ -719,5 +719,5 @@ SharedWeakUnion<T>::unsafeReleaseNoStore()
}
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <atomic>
#include <cstdint>
namespace ripple {
namespace xrpl {
/** Action to perform when releasing a strong pointer.
@@ -34,7 +34,7 @@ enum class ReleaseWeakRefAction { noop, destroy };
/** Implement the strong count, weak count, and bit flags for an intrusive
pointer.
A class can satisfy the requirements of a ripple::IntrusivePointer by
A class can satisfy the requirements of a xrpl::IntrusivePointer by
inheriting from this class.
*/
struct IntrusiveRefCounts
@@ -257,7 +257,7 @@ IntrusiveRefCounts::releaseStrongRef() const
RefCountPair const prevVal{prevIntVal};
XRPL_ASSERT(
(prevVal.strong >= strongDelta),
"ripple::IntrusiveRefCounts::releaseStrongRef : previous ref "
"xrpl::IntrusiveRefCounts::releaseStrongRef : previous ref "
"higher than new");
auto nextIntVal = prevIntVal - strongDelta;
ReleaseStrongRefAction action = noop;
@@ -282,7 +282,7 @@ IntrusiveRefCounts::releaseStrongRef() const
// twice.
XRPL_ASSERT(
(action == noop) || !(prevIntVal & partialDestroyStartedMask),
"ripple::IntrusiveRefCounts::releaseStrongRef : not in partial "
"xrpl::IntrusiveRefCounts::releaseStrongRef : not in partial "
"destroy");
return action;
}
@@ -314,7 +314,7 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
// can't happen twice.
XRPL_ASSERT(
(!prevVal.partialDestroyStartedBit),
"ripple::IntrusiveRefCounts::addWeakReleaseStrongRef : not in "
"xrpl::IntrusiveRefCounts::addWeakReleaseStrongRef : not in "
"partial destroy");
auto nextIntVal = prevIntVal + delta;
@@ -336,7 +336,7 @@ IntrusiveRefCounts::addWeakReleaseStrongRef() const
{
XRPL_ASSERT(
(!(prevIntVal & partialDestroyStartedMask)),
"ripple::IntrusiveRefCounts::addWeakReleaseStrongRef : not "
"xrpl::IntrusiveRefCounts::addWeakReleaseStrongRef : not "
"started partial destroy");
return action;
}
@@ -408,11 +408,11 @@ inline IntrusiveRefCounts::~IntrusiveRefCounts() noexcept
auto v = refCounts.load(std::memory_order_acquire);
XRPL_ASSERT(
(!(v & valueMask)),
"ripple::IntrusiveRefCounts::~IntrusiveRefCounts : count must be zero");
"xrpl::IntrusiveRefCounts::~IntrusiveRefCounts : count must be zero");
auto t = v & tagMask;
XRPL_ASSERT(
(!t || t == tagMask),
"ripple::IntrusiveRefCounts::~IntrusiveRefCounts : valid tag");
"xrpl::IntrusiveRefCounts::~IntrusiveRefCounts : valid tag");
#endif
}
@@ -427,7 +427,7 @@ inline IntrusiveRefCounts::RefCountPair::RefCountPair(
{
XRPL_ASSERT(
(strong < checkStrongMaxValue && weak < checkWeakMaxValue),
"ripple::IntrusiveRefCounts::RefCountPair(FieldType) : inputs inside "
"xrpl::IntrusiveRefCounts::RefCountPair(FieldType) : inputs inside "
"range");
}
@@ -438,7 +438,7 @@ inline IntrusiveRefCounts::RefCountPair::RefCountPair(
{
XRPL_ASSERT(
(strong < checkStrongMaxValue && weak < checkWeakMaxValue),
"ripple::IntrusiveRefCounts::RefCountPair(CountType, CountType) : "
"xrpl::IntrusiveRefCounts::RefCountPair(CountType, CountType) : "
"inputs inside range");
}
@@ -447,7 +447,7 @@ IntrusiveRefCounts::RefCountPair::combinedValue() const noexcept
{
XRPL_ASSERT(
(strong < checkStrongMaxValue && weak < checkWeakMaxValue),
"ripple::IntrusiveRefCounts::RefCountPair::combinedValue : inputs "
"xrpl::IntrusiveRefCounts::RefCountPair::combinedValue : inputs "
"inside range");
return (static_cast<IntrusiveRefCounts::FieldType>(weak)
<< IntrusiveRefCounts::StrongCountNumBits) |
@@ -465,7 +465,7 @@ partialDestructorFinished(T** o)
XRPL_ASSERT(
(!p.partialDestroyFinishedBit && p.partialDestroyStartedBit &&
!p.strong),
"ripple::partialDestructorFinished : not a weak ref");
"xrpl::partialDestructorFinished : not a weak ref");
if (!p.weak)
{
// There was a weak count before the partial destructor ran (or we would
@@ -479,5 +479,5 @@ partialDestructorFinished(T** o)
}
//------------------------------------------------------------------------------
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,10 +4,10 @@
#include <xrpl/basics/TaggedCache.h>
#include <xrpl/basics/base_uint.h>
namespace ripple {
namespace xrpl {
using KeyCache = TaggedCache<uint256, int, true>;
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_KEYCACHE_H

View File

@@ -6,7 +6,7 @@
#include <memory>
#include <unordered_map>
namespace ripple {
namespace xrpl {
namespace detail {
@@ -109,6 +109,6 @@ LocalValue<T>::operator*()
.emplace(this, std::make_unique<detail::LocalValues::Value<T>>(t_))
.first->second->get());
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -13,7 +13,7 @@
#include <mutex>
#include <utility>
namespace ripple {
namespace xrpl {
// DEPRECATED use beast::severities::Severity instead
enum LogSeverity {
@@ -271,6 +271,6 @@ setDebugLogSink(std::unique_ptr<beast::Journal::Sink> sink);
beast::Journal
debugLog();
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <cassert>
#include <cstddef>
namespace ripple {
namespace xrpl {
/** Calculate one number divided by another number in percentage.
* The result is rounded up to the next integer, and capped in the range [0,100]
@@ -44,6 +44,6 @@ static_assert(calculatePercent(50'000'000, 100'000'000) == 50);
static_assert(calculatePercent(50'000'001, 100'000'000) == 51);
static_assert(calculatePercent(99'999'999, 100'000'000) == 100);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <ostream>
#include <string>
namespace ripple {
namespace xrpl {
class Number;
@@ -402,6 +402,6 @@ public:
operator=(NumberRoundModeGuard const&) = delete;
};
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_NUMBER_H_INCLUDED

View File

@@ -21,11 +21,11 @@ ripple/basic should contain no dependencies on other modules.
- `std::set`
- For sorted containers.
- `ripple::hash_set`
- `xrpl::hash_set`
- Where inserts and contains need to be O(1).
- For "small" sets, `std::set` might be faster and smaller.
- `ripple::hardened_hash_set`
- `xrpl::hardened_hash_set`
- For data sets where the key could be manipulated by an attacker
in an attempt to mount an algorithmic complexity attack: see
http://en.wikipedia.org/wiki/Algorithmic_complexity_attack
@@ -33,5 +33,5 @@ ripple/basic should contain no dependencies on other modules.
The following container is deprecated
- `std::unordered_set`
- Use `ripple::hash_set` instead, which uses a better hashing algorithm.
- Or use `ripple::hardened_hash_set` to prevent algorithmic complexity attacks.
- Use `xrpl::hash_set` instead, which uses a better hashing algorithm.
- Or use `xrpl::hardened_hash_set` to prevent algorithmic complexity attacks.

View File

@@ -11,7 +11,7 @@
#include <string>
#include <vector>
namespace ripple {
namespace xrpl {
/** A closed interval over the domain T.
@@ -85,7 +85,7 @@ to_string(RangeSet<T> const& rs)
std::string s;
for (auto const& interval : rs)
s += ripple::to_string(interval) + ",";
s += xrpl::to_string(interval) + ",";
s.pop_back();
return s;
@@ -172,6 +172,6 @@ prevMissing(RangeSet<T> const& rs, T t, T minVal = 0)
return boost::icl::last(tgt);
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <functional>
#include <vector>
namespace ripple {
namespace xrpl {
class Resolver
{
@@ -47,6 +47,6 @@ public:
/** @} */
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <boost/asio/io_context.hpp>
namespace ripple {
namespace xrpl {
class ResolverAsio : public Resolver
{
@@ -17,6 +17,6 @@ public:
New(boost::asio::io_context&, beast::Journal);
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <ostream>
namespace ripple {
namespace xrpl {
// A SHAMapHash is the hash of a node in a SHAMap, and also the
// type of the hash of the entire SHAMap.
@@ -97,6 +97,6 @@ extract(SHAMapHash const& key)
return *reinterpret_cast<std::size_t const*>(key.as_uint256().data());
}
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_SHAMAP_HASH_H_INCLUDED

View File

@@ -4,7 +4,7 @@
#include <memory>
#include <variant>
namespace ripple {
namespace xrpl {
/** A combination of a std::shared_ptr and a std::weak_pointer.
@@ -112,5 +112,5 @@ public:
private:
std::variant<std::shared_ptr<T>, std::weak_ptr<T>> combo_;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <xrpl/basics/SharedWeakCachePointer.h>
namespace ripple {
namespace xrpl {
template <class T>
SharedWeakCachePointer<T>::SharedWeakCachePointer(
SharedWeakCachePointer const& rhs) = default;
@@ -169,5 +169,5 @@ SharedWeakCachePointer<T>::convertToWeak()
return false;
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -22,7 +22,7 @@
#include <sys/mman.h>
#endif
namespace ripple {
namespace xrpl {
template <typename Type>
class SlabAllocator
@@ -128,7 +128,7 @@ class SlabAllocator
{
XRPL_ASSERT(
own(ptr),
"ripple::SlabAllocator::SlabBlock::deallocate : own input");
"xrpl::SlabAllocator::SlabBlock::deallocate : own input");
std::lock_guard l(m_);
@@ -173,7 +173,7 @@ public:
{
XRPL_ASSERT(
(itemAlignment_ & (itemAlignment_ - 1)) == 0,
"ripple::SlabAllocator::SlabAllocator : valid alignment");
"xrpl::SlabAllocator::SlabAllocator : valid alignment");
}
SlabAllocator(SlabAllocator const& other) = delete;
@@ -285,7 +285,7 @@ public:
{
XRPL_ASSERT(
ptr,
"ripple::SlabAllocator::SlabAllocator::deallocate : non-null "
"xrpl::SlabAllocator::SlabAllocator::deallocate : non-null "
"input");
for (auto slab = slabs_.load(); slab != nullptr; slab = slab->next_)
@@ -419,6 +419,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_SLABALLOCATOR_H_INCLUDED

View File

@@ -15,7 +15,7 @@
#include <type_traits>
#include <vector>
namespace ripple {
namespace xrpl {
/** An immutable linear range of bytes.
@@ -87,7 +87,7 @@ public:
{
XRPL_ASSERT(
i < size_,
"ripple::Slice::operator[](std::size_t) const : valid input");
"xrpl::Slice::operator[](std::size_t) const : valid input");
return data_[i];
}
@@ -243,6 +243,6 @@ makeSlice(std::basic_string<char, Traits, Alloc> const& s)
return Slice(s.data(), s.size());
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -12,7 +12,7 @@
#include <optional>
#include <string>
namespace ripple {
namespace xrpl {
/** Format arbitrary binary data as an SQLite "blob literal".
@@ -132,6 +132,6 @@ to_uint64(std::string const& s);
bool
isProperlyFormedTomlDomain(std::string_view domain);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -16,7 +16,7 @@
#include <type_traits>
#include <vector>
namespace ripple {
namespace xrpl {
/** Map/cache combination.
This class implements a cache and a map. The cache keeps objects alive
@@ -315,6 +315,6 @@ private:
std::uint64_t m_misses;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <xrpl/basics/IntrusivePointer.ipp>
#include <xrpl/basics/TaggedCache.h>
namespace ripple {
namespace xrpl {
template <
class Key,
@@ -1005,6 +1005,6 @@ TaggedCache<
});
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <string>
#include <type_traits>
namespace ripple {
namespace xrpl {
/** to_string() generalizes std::to_string to handle bools, chars, and strings.
@@ -43,6 +43,6 @@ to_string(char const* s)
return s;
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -22,7 +22,7 @@
* what container it is.
*/
namespace ripple {
namespace xrpl {
// hash containers
@@ -102,6 +102,6 @@ template <
using hardened_hash_multiset =
std::unordered_multiset<Value, Hash, Pred, Allocator>;
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <ratio>
#include <thread>
namespace ripple {
namespace xrpl {
/** Tracks program uptime to seconds precision.
@@ -45,6 +45,6 @@ private:
start_clock();
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <utility>
namespace ripple {
namespace xrpl {
// Requires: [first1, last1) and [first2, last2) are ordered ranges according to
// comp.
@@ -95,6 +95,6 @@ remove_if_intersect_or_match(
return first1;
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -38,7 +38,7 @@
#include <cstdint>
#include <string>
namespace ripple {
namespace xrpl {
std::string
base64_encode(std::uint8_t const* data, std::size_t len);
@@ -53,6 +53,6 @@ base64_encode(std::string const& s)
std::string
base64_decode(std::string_view data);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -23,7 +23,7 @@
#include <cstring>
#include <type_traits>
namespace ripple {
namespace xrpl {
namespace detail {
@@ -275,7 +275,7 @@ public:
{
XRPL_ASSERT(
c.size() * sizeof(typename Container::value_type) == size(),
"ripple::base_uint::base_uint(Container auto) : input size match");
"xrpl::base_uint::base_uint(Container auto) : input size match");
std::memcpy(data_.data(), c.data(), size());
}
@@ -288,7 +288,7 @@ public:
{
XRPL_ASSERT(
c.size() * sizeof(typename Container::value_type) == size(),
"ripple::base_uint::operator=(Container auto) : input size match");
"xrpl::base_uint::operator=(Container auto) : input size match");
std::memcpy(data_.data(), c.data(), size());
return *this;
}
@@ -648,12 +648,12 @@ static_assert(sizeof(uint192) == 192 / 8, "There should be no padding bytes");
static_assert(sizeof(uint256) == 256 / 8, "There should be no padding bytes");
#endif
} // namespace ripple
} // namespace xrpl
namespace beast {
template <std::size_t Bits, class Tag>
struct is_uniquely_represented<ripple::base_uint<Bits, Tag>>
struct is_uniquely_represented<xrpl::base_uint<Bits, Tag>>
: public std::true_type
{
explicit is_uniquely_represented() = default;

View File

@@ -12,7 +12,7 @@
#include <ratio>
#include <string>
namespace ripple {
namespace xrpl {
// A few handy aliases
@@ -104,6 +104,6 @@ stopwatch()
return beast::get_abstract_clock<Facade, Clock>();
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <functional>
namespace ripple {
namespace xrpl {
#ifdef _MSC_VER
@@ -52,6 +52,6 @@ using equal_to = std::equal_to<T>;
#endif
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -7,7 +7,7 @@
#include <string>
#include <utility>
namespace ripple {
namespace xrpl {
/* Programming By Contract
@@ -52,6 +52,6 @@ Throw(Args&&... args)
[[noreturn]] void
LogicError(std::string const& how) noexcept;
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -9,7 +9,7 @@
#include <random>
#include <utility>
namespace ripple {
namespace xrpl {
namespace detail {
@@ -92,6 +92,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <string>
namespace ripple {
namespace xrpl {
template <class Stream, class Iter>
Stream&
@@ -85,6 +85,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <string>
namespace ripple {
namespace xrpl {
/** Create a self-signed SSL context that allows anonymous Diffie Hellman. */
std::shared_ptr<boost::asio::ssl::context>
@@ -19,6 +19,6 @@ make_SSLContextAuthed(
std::string const& chainFile,
std::string const& cipherList);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <limits>
#include <optional>
namespace ripple {
namespace xrpl {
auto constexpr muldiv_max = std::numeric_limits<std::uint64_t>::max();
/** Return value*mul/div accurately.
@@ -21,6 +21,6 @@ auto constexpr muldiv_max = std::numeric_limits<std::uint64_t>::max();
std::optional<std::uint64_t>
mulDiv(std::uint64_t value, std::uint64_t mul, std::uint64_t div);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -12,7 +12,7 @@
#include <utility>
#include <vector>
namespace ripple {
namespace xrpl {
template <typename Key>
static std::size_t
@@ -242,7 +242,7 @@ public:
map_.resize(partitions_);
XRPL_ASSERT(
partitions_,
"ripple::partitioned_unordered_map::partitioned_unordered_map : "
"xrpl::partitioned_unordered_map::partitioned_unordered_map : "
"nonzero partitions");
}
@@ -401,6 +401,6 @@ private:
mutable partition_map_type map_{};
};
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_PARTITIONED_UNORDERED_MAP_H

View File

@@ -11,7 +11,7 @@
#include <random>
#include <type_traits>
namespace ripple {
namespace xrpl {
#ifndef __INTELLISENSE__
static_assert(
@@ -95,7 +95,7 @@ std::enable_if_t<
Integral>
rand_int(Engine& engine, Integral min, Integral max)
{
XRPL_ASSERT(max > min, "ripple::rand_int : max over min inputs");
XRPL_ASSERT(max > min, "xrpl::rand_int : max over min inputs");
// This should have no state and constructing it should
// be very cheap. If that turns out not to be the case
@@ -186,6 +186,6 @@ rand_bool()
}
/** @} */
} // namespace ripple
} // namespace xrpl
#endif // XRPL_BASICS_RANDOM_H_INCLUDED

View File

@@ -3,7 +3,7 @@
#include <type_traits>
namespace ripple {
namespace xrpl {
// safe_cast adds compile-time checks to a static_cast to ensure that
// the destination can hold all values of the source. This is particularly
@@ -80,6 +80,6 @@ inline constexpr std::
return unsafe_cast<Dest>(static_cast<std::underlying_type_t<Src>>(s));
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -8,7 +8,7 @@
#include <type_traits>
#include <utility>
namespace ripple {
namespace xrpl {
// RAII scope helpers. As specified in Library Fundamental, Version 3
// Basic design of idea: https://www.youtube.com/watch?v=WjTrfoiB0MQ
@@ -218,7 +218,7 @@ public:
{
XRPL_ASSERT(
plock->owns_lock(),
"ripple::scope_unlock::scope_unlock : mutex must be locked");
"xrpl::scope_unlock::scope_unlock : mutex must be locked");
plock->unlock();
}
@@ -236,6 +236,6 @@ public:
template <class Mutex>
scope_unlock(std::unique_lock<Mutex>&) -> scope_unlock<Mutex>;
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -13,7 +13,7 @@
#include <immintrin.h>
#endif
namespace ripple {
namespace xrpl {
namespace detail {
/** Inform the processor that we are in a tight spin-wait loop.
@@ -105,7 +105,7 @@ public:
{
XRPL_ASSERT(
index >= 0 && (mask_ != 0),
"ripple::packed_spinlock::packed_spinlock : valid index and mask");
"xrpl::packed_spinlock::packed_spinlock : valid index and mask");
}
[[nodiscard]] bool
@@ -206,6 +206,6 @@ public:
};
/** @} */
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <boost/algorithm/hex.hpp>
#include <boost/endian/conversion.hpp>
namespace ripple {
namespace xrpl {
template <class FwdIt>
std::string
@@ -28,6 +28,6 @@ strHex(T const& from)
return strHex(from.begin(), from.end());
}
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -10,7 +10,7 @@
#include <iostream>
#include <type_traits>
namespace ripple {
namespace xrpl {
/** A type-safe wrap around standard integral types
@@ -197,11 +197,11 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
namespace beast {
template <class Int, class Tag, class HashAlgorithm>
struct is_contiguously_hashable<ripple::tagged_integer<Int, Tag>, HashAlgorithm>
struct is_contiguously_hashable<xrpl::tagged_integer<Int, Tag>, HashAlgorithm>
: public is_contiguously_hashable<Int, HashAlgorithm>
{
explicit is_contiguously_hashable() = default;

View File

@@ -0,0 +1,207 @@
#ifndef XRPL_CORE_CLOSURE_COUNTER_H_INCLUDED
#define XRPL_CORE_CLOSURE_COUNTER_H_INCLUDED
#include <xrpl/basics/Log.h>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <optional>
namespace xrpl {
/**
* The role of a `ClosureCounter` is to assist in shutdown by letting callers
* wait for the completion of closures (of a specific type signature) that they
* previously registered. These closures are typically callbacks for
* asynchronous operations. The lifetime of a `ClosureCounter` consists of two
* phases: the initial expanding "fork" phase, and the subsequent shrinking
* "join" phase.
*
* In the fork phase, callers register a closure by passing the closure and
* receiving a substitute in return. The substitute has the same callable
* interface as the closure, and it informs the `ClosureCounter` whenever it
* is copied or destroyed, so that it can keep an accurate count of copies.
*
* The transition to the join phase is made by a call to `join`. In this
* phase, every substitute returned going forward will be null, signaling to
* the caller that they should drop the closure and cancel their operation.
* `join` blocks until all existing closure substitutes are destroyed.
*
* \tparam Ret_t The return type of the closure.
* \tparam Args_t The argument types of the closure.
*/
template <typename Ret_t, typename... Args_t>
class ClosureCounter
{
private:
std::mutex mutable mutex_{};
std::condition_variable allClosuresDoneCond_{}; // guard with mutex_
bool waitForClosures_{false}; // guard with mutex_
std::atomic<int> closureCount_{0};
// Increment the count.
ClosureCounter&
operator++()
{
++closureCount_;
return *this;
}
// Decrement the count. If we're stopping and the count drops to zero
// notify allClosuresDoneCond_.
ClosureCounter&
operator--()
{
// Even though closureCount_ is atomic, we decrement its value under
// a lock. This removes a small timing window that occurs if the
// waiting thread is handling a spurious wakeup when closureCount_
// drops to zero.
std::lock_guard lock{mutex_};
// Update closureCount_. Notify if stopping and closureCount_ == 0.
if ((--closureCount_ == 0) && waitForClosures_)
allClosuresDoneCond_.notify_all();
return *this;
}
// A private template class that helps count the number of closures
// in flight. This allows callers to block until all their postponed
// closures are dispatched.
template <typename Closure>
class Substitute
{
private:
ClosureCounter& counter_;
std::remove_reference_t<Closure> closure_;
static_assert(
std::is_same<decltype(closure_(std::declval<Args_t>()...)), Ret_t>::
value,
"Closure arguments don't match ClosureCounter Ret_t or Args_t");
public:
Substitute() = delete;
Substitute(Substitute const& rhs)
: counter_(rhs.counter_), closure_(rhs.closure_)
{
++counter_;
}
Substitute(Substitute&& rhs) noexcept(
std::is_nothrow_move_constructible<Closure>::value)
: counter_(rhs.counter_), closure_(std::move(rhs.closure_))
{
++counter_;
}
Substitute(ClosureCounter& counter, Closure&& closure)
: counter_(counter), closure_(std::forward<Closure>(closure))
{
++counter_;
}
Substitute&
operator=(Substitute const& rhs) = delete;
Substitute&
operator=(Substitute&& rhs) = delete;
~Substitute()
{
--counter_;
}
// Note that Args_t is not deduced, it is explicit. So Args_t&&
// would be an rvalue reference, not a forwarding reference. We
// want to forward exactly what the user declared.
Ret_t
operator()(Args_t... args)
{
return closure_(std::forward<Args_t>(args)...);
}
};
public:
ClosureCounter() = default;
// Not copyable or movable. Outstanding counts would be hard to sort out.
ClosureCounter(ClosureCounter const&) = delete;
ClosureCounter&
operator=(ClosureCounter const&) = delete;
/** Destructor verifies all in-flight closures are complete. */
~ClosureCounter()
{
using namespace std::chrono_literals;
join("ClosureCounter", 1s, debugLog());
}
/** Returns once all counted in-flight closures are destroyed.
@param name Name reported if join time exceeds wait.
@param wait If join() exceeds this duration report to Journal.
@param j Journal written to if wait is exceeded.
*/
void
join(char const* name, std::chrono::milliseconds wait, beast::Journal j)
{
std::unique_lock<std::mutex> lock{mutex_};
waitForClosures_ = true;
if (closureCount_ > 0)
{
if (!allClosuresDoneCond_.wait_for(
lock, wait, [this] { return closureCount_ == 0; }))
{
if (auto stream = j.error())
stream << name << " waiting for ClosureCounter::join().";
allClosuresDoneCond_.wait(
lock, [this] { return closureCount_ == 0; });
}
}
}
/** Wrap the passed closure with a reference counter.
@param closure Closure that accepts Args_t parameters and returns Ret_t.
@return If join() has been called returns std::nullopt. Otherwise
returns a std::optional that wraps closure with a
reference counter.
*/
template <class Closure>
std::optional<Substitute<Closure>>
wrap(Closure&& closure)
{
std::optional<Substitute<Closure>> ret;
std::lock_guard lock{mutex_};
if (!waitForClosures_)
ret.emplace(*this, std::forward<Closure>(closure));
return ret;
}
/** Current number of Closures outstanding. Only useful for testing. */
int
count() const
{
return closureCount_;
}
/** Returns true if this has been joined.
Even if true is returned, counted closures may still be in flight.
However if (joined() && (count() == 0)) there should be no more
counted closures in flight.
*/
bool
joined() const
{
std::lock_guard lock{mutex_};
return waitForClosures_;
}
};
} // namespace xrpl
#endif // XRPL_CORE_CLOSURE_COUNTER_H_INCLUDED

133
include/xrpl/core/Coro.ipp Normal file
View File

@@ -0,0 +1,133 @@
#ifndef XRPL_CORE_COROINL_H_INCLUDED
#define XRPL_CORE_COROINL_H_INCLUDED
#include <xrpl/basics/ByteUtilities.h>
namespace xrpl {
template <class F>
JobQueue::Coro::Coro(
Coro_create_t,
JobQueue& jq,
JobType type,
std::string const& name,
F&& f)
: jq_(jq)
, type_(type)
, name_(name)
, running_(false)
, coro_(
[this, fn = std::forward<F>(f)](
boost::coroutines::asymmetric_coroutine<void>::push_type&
do_yield) {
yield_ = &do_yield;
yield();
fn(shared_from_this());
#ifndef NDEBUG
finished_ = true;
#endif
},
boost::coroutines::attributes(megabytes(1)))
{
}
inline JobQueue::Coro::~Coro()
{
#ifndef NDEBUG
XRPL_ASSERT(finished_, "xrpl::JobQueue::Coro::~Coro : is finished");
#endif
}
inline void
JobQueue::Coro::yield() const
{
{
std::lock_guard lock(jq_.m_mutex);
++jq_.nSuspend_;
}
(*yield_)();
}
inline bool
JobQueue::Coro::post()
{
{
std::lock_guard lk(mutex_run_);
running_ = true;
}
// sp keeps 'this' alive
if (jq_.addJob(
type_, name_, [this, sp = shared_from_this()]() { resume(); }))
{
return true;
}
// The coroutine will not run. Clean up running_.
std::lock_guard lk(mutex_run_);
running_ = false;
cv_.notify_all();
return false;
}
inline void
JobQueue::Coro::resume()
{
{
std::lock_guard lk(mutex_run_);
running_ = true;
}
{
std::lock_guard lock(jq_.m_mutex);
--jq_.nSuspend_;
}
auto saved = detail::getLocalValues().release();
detail::getLocalValues().reset(&lvs_);
std::lock_guard lock(mutex_);
XRPL_ASSERT(
static_cast<bool>(coro_), "xrpl::JobQueue::Coro::resume : is runnable");
coro_();
detail::getLocalValues().release();
detail::getLocalValues().reset(saved);
std::lock_guard lk(mutex_run_);
running_ = false;
cv_.notify_all();
}
inline bool
JobQueue::Coro::runnable() const
{
return static_cast<bool>(coro_);
}
inline void
JobQueue::Coro::expectEarlyExit()
{
#ifndef NDEBUG
if (!finished_)
#endif
{
// expectEarlyExit() must only ever be called from outside the
// Coro's stack. It you're inside the stack you can simply return
// and be done.
//
// That said, since we're outside the Coro's stack, we need to
// decrement the nSuspend that the Coro's call to yield caused.
std::lock_guard lock(jq_.m_mutex);
--jq_.nSuspend_;
#ifndef NDEBUG
finished_ = true;
#endif
}
}
inline void
JobQueue::Coro::join()
{
std::unique_lock<std::mutex> lk(mutex_run_);
cv_.wait(lk, [this]() { return running_ == false; });
}
} // namespace xrpl
#endif

136
include/xrpl/core/Job.h Normal file
View File

@@ -0,0 +1,136 @@
#ifndef XRPL_CORE_JOB_H_INCLUDED
#define XRPL_CORE_JOB_H_INCLUDED
#include <xrpl/basics/CountedObject.h>
#include <xrpl/core/ClosureCounter.h>
#include <xrpl/core/LoadMonitor.h>
#include <functional>
namespace xrpl {
// Note that this queue should only be used for CPU-bound jobs
// It is primarily intended for signature checking
enum JobType {
// Special type indicating an invalid job - will go away soon.
jtINVALID = -1,
// Job types - the position in this enum indicates the job priority with
// earlier jobs having lower priority than later jobs. If you wish to
// insert a job at a specific priority, simply add it at the right location.
jtPACK, // Make a fetch pack for a peer
jtPUBOLDLEDGER, // An old ledger has been accepted
jtCLIENT, // A placeholder for the priority of all jtCLIENT jobs
jtCLIENT_SUBSCRIBE, // A websocket subscription by a client
jtCLIENT_FEE_CHANGE, // Subscription for fee change by a client
jtCLIENT_CONSENSUS, // Subscription for consensus state change by a client
jtCLIENT_ACCT_HIST, // Subscription for account history by a client
jtCLIENT_RPC, // Client RPC request
jtCLIENT_WEBSOCKET, // Client websocket request
jtRPC, // A websocket command from the client
jtSWEEP, // Sweep for stale structures
jtVALIDATION_ut, // A validation from an untrusted source
jtMANIFEST, // A validator's manifest
jtUPDATE_PF, // Update pathfinding requests
jtTRANSACTION_l, // A local transaction
jtREPLAY_REQ, // Peer request a ledger delta or a skip list
jtLEDGER_REQ, // Peer request ledger/txnset data
jtPROPOSAL_ut, // A proposal from an untrusted source
jtREPLAY_TASK, // A Ledger replay task/subtask
jtTRANSACTION, // A transaction received from the network
jtMISSING_TXN, // Request missing transactions
jtREQUESTED_TXN, // Reply with requested transactions
jtBATCH, // Apply batched transactions
jtLEDGER_DATA, // Received data for a ledger we're acquiring
jtADVANCE, // Advance validated/acquired ledgers
jtPUBLEDGER, // Publish a fully-accepted ledger
jtTXN_DATA, // Fetch a proposed set
jtWAL, // Write-ahead logging
jtVALIDATION_t, // A validation from a trusted source
jtWRITE, // Write out hashed objects
jtACCEPT, // Accept a consensus ledger
jtPROPOSAL_t, // A proposal from a trusted source
jtNETOP_CLUSTER, // NetworkOPs cluster peer report
jtNETOP_TIMER, // NetworkOPs net timer processing
jtADMIN, // An administrative operation
// Special job types which are not dispatched by the job pool
jtPEER,
jtDISK,
jtTXN_PROC,
jtOB_SETUP,
jtPATH_FIND,
jtHO_READ,
jtHO_WRITE,
jtGENERIC, // Used just to measure time
// Node store monitoring
jtNS_SYNC_READ,
jtNS_ASYNC_READ,
jtNS_WRITE,
};
class Job : public CountedObject<Job>
{
public:
using clock_type = std::chrono::steady_clock;
/** Default constructor.
Allows Job to be used as a container type.
This is used to allow things like jobMap [key] = value.
*/
// VFALCO NOTE I'd prefer not to have a default constructed object.
// What is the semantic meaning of a Job with no associated
// function? Having the invariant "all Job objects refer to
// a job" would reduce the number of states.
//
Job();
Job(JobType type, std::uint64_t index);
// VFALCO TODO try to remove the dependency on LoadMonitor.
Job(JobType type,
std::string const& name,
std::uint64_t index,
LoadMonitor& lm,
std::function<void()> const& job);
JobType
getType() const;
/** Returns the time when the job was queued. */
clock_type::time_point const&
queue_time() const;
void
doJob();
// These comparison operators make the jobs sort in priority order
// in the job set
bool
operator<(Job const& j) const;
bool
operator>(Job const& j) const;
bool
operator<=(Job const& j) const;
bool
operator>=(Job const& j) const;
private:
JobType mType;
std::uint64_t mJobIndex;
std::function<void()> mJob;
std::shared_ptr<LoadEvent> m_loadEvent;
std::string mName;
clock_type::time_point m_queue_time;
};
using JobCounter = ClosureCounter<void>;
} // namespace xrpl
#endif

View File

@@ -0,0 +1,413 @@
#ifndef XRPL_CORE_JOBQUEUE_H_INCLUDED
#define XRPL_CORE_JOBQUEUE_H_INCLUDED
#include <xrpl/basics/LocalValue.h>
#include <xrpl/core/ClosureCounter.h>
#include <xrpl/core/JobTypeData.h>
#include <xrpl/core/JobTypes.h>
#include <xrpl/core/detail/Workers.h>
#include <xrpl/json/json_value.h>
#include <boost/coroutine/all.hpp>
#include <set>
namespace xrpl {
namespace perf {
class PerfLog;
}
class Logs;
struct Coro_create_t
{
explicit Coro_create_t() = default;
};
/** A pool of threads to perform work.
A job posted will always run to completion.
Coroutines that are suspended must be resumed,
and run to completion.
When the JobQueue stops, it waits for all jobs
and coroutines to finish.
*/
class JobQueue : private Workers::Callback
{
public:
/** Coroutines must run to completion. */
class Coro : public std::enable_shared_from_this<Coro>
{
private:
detail::LocalValues lvs_;
JobQueue& jq_;
JobType type_;
std::string name_;
bool running_;
std::mutex mutex_;
std::mutex mutex_run_;
std::condition_variable cv_;
boost::coroutines::asymmetric_coroutine<void>::pull_type coro_;
boost::coroutines::asymmetric_coroutine<void>::push_type* yield_;
#ifndef NDEBUG
bool finished_ = false;
#endif
public:
// Private: Used in the implementation
template <class F>
Coro(Coro_create_t, JobQueue&, JobType, std::string const&, F&&);
// Not copy-constructible or assignable
Coro(Coro const&) = delete;
Coro&
operator=(Coro const&) = delete;
~Coro();
/** Suspend coroutine execution.
Effects:
The coroutine's stack is saved.
The associated Job thread is released.
Note:
The associated Job function returns.
Undefined behavior if called consecutively without a corresponding
post.
*/
void
yield() const;
/** Schedule coroutine execution.
Effects:
Returns immediately.
A new job is scheduled to resume the execution of the coroutine.
When the job runs, the coroutine's stack is restored and execution
continues at the beginning of coroutine function or the
statement after the previous call to yield. Undefined behavior if
called after the coroutine has completed with a return (as opposed to
a yield()). Undefined behavior if post() or resume() called
consecutively without a corresponding yield.
@return true if the Coro's job is added to the JobQueue.
*/
bool
post();
/** Resume coroutine execution.
Effects:
The coroutine continues execution from where it last left off
using this same thread.
Undefined behavior if called after the coroutine has completed
with a return (as opposed to a yield()).
Undefined behavior if resume() or post() called consecutively
without a corresponding yield.
*/
void
resume();
/** Returns true if the Coro is still runnable (has not returned). */
bool
runnable() const;
/** Once called, the Coro allows early exit without an assert. */
void
expectEarlyExit();
/** Waits until coroutine returns from the user function. */
void
join();
};
using JobFunction = std::function<void()>;
JobQueue(
int threadCount,
beast::insight::Collector::ptr const& collector,
beast::Journal journal,
Logs& logs,
perf::PerfLog& perfLog);
~JobQueue();
/** Adds a job to the JobQueue.
@param type The type of job.
@param name Name of the job.
@param jobHandler Lambda with signature void (Job&). Called when the
job is executed.
@return true if jobHandler added to queue.
*/
template <
typename JobHandler,
typename = std::enable_if_t<std::is_same<
decltype(std::declval<JobHandler&&>()()),
void>::value>>
bool
addJob(JobType type, std::string const& name, JobHandler&& jobHandler)
{
if (auto optionalCountedJob =
jobCounter_.wrap(std::forward<JobHandler>(jobHandler)))
{
return addRefCountedJob(type, name, std::move(*optionalCountedJob));
}
return false;
}
/** Creates a coroutine and adds a job to the queue which will run it.
@param t The type of job.
@param name Name of the job.
@param f Has a signature of void(std::shared_ptr<Coro>). Called when the
job executes.
@return shared_ptr to posted Coro. nullptr if post was not successful.
*/
template <class F>
std::shared_ptr<Coro>
postCoro(JobType t, std::string const& name, F&& f);
/** Jobs waiting at this priority.
*/
int
getJobCount(JobType t) const;
/** Jobs waiting plus running at this priority.
*/
int
getJobCountTotal(JobType t) const;
/** All waiting jobs at or greater than this priority.
*/
int
getJobCountGE(JobType t) const;
/** Return a scoped LoadEvent.
*/
std::unique_ptr<LoadEvent>
makeLoadEvent(JobType t, std::string const& name);
/** Add multiple load events.
*/
void
addLoadEvents(JobType t, int count, std::chrono::milliseconds elapsed);
// Cannot be const because LoadMonitor has no const methods.
bool
isOverloaded();
// Cannot be const because LoadMonitor has no const methods.
Json::Value
getJson(int c = 0);
/** Block until no jobs running. */
void
rendezvous();
void
stop();
bool
isStopping() const
{
return stopping_;
}
// We may be able to move away from this, but we can keep it during the
// transition.
bool
isStopped() const;
private:
friend class Coro;
using JobDataMap = std::map<JobType, JobTypeData>;
beast::Journal m_journal;
mutable std::mutex m_mutex;
std::uint64_t m_lastJob;
std::set<Job> m_jobSet;
JobCounter jobCounter_;
std::atomic_bool stopping_{false};
std::atomic_bool stopped_{false};
JobDataMap m_jobData;
JobTypeData m_invalidJobData;
// The number of jobs currently in processTask()
int m_processCount;
// The number of suspended coroutines
int nSuspend_ = 0;
Workers m_workers;
// Statistics tracking
perf::PerfLog& perfLog_;
beast::insight::Collector::ptr m_collector;
beast::insight::Gauge job_count;
beast::insight::Hook hook;
std::condition_variable cv_;
void
collect();
JobTypeData&
getJobTypeData(JobType type);
// Adds a reference counted job to the JobQueue.
//
// param type The type of job.
// param name Name of the job.
// param func std::function with signature void (Job&). Called when the
// job is executed.
//
// return true if func added to queue.
bool
addRefCountedJob(
JobType type,
std::string const& name,
JobFunction const& func);
// Returns the next Job we should run now.
//
// RunnableJob:
// A Job in the JobSet whose slots count for its type is greater than zero.
//
// Pre-conditions:
// mJobSet must not be empty.
// mJobSet holds at least one RunnableJob
//
// Post-conditions:
// job is a valid Job object.
// job is removed from mJobQueue.
// Waiting job count of its type is decremented
// Running job count of its type is incremented
//
// Invariants:
// The calling thread owns the JobLock
void
getNextJob(Job& job);
// Indicates that a running Job has completed its task.
//
// Pre-conditions:
// Job must not exist in mJobSet.
// The JobType must not be invalid.
//
// Post-conditions:
// The running count of that JobType is decremented
// A new task is signaled if there are more waiting Jobs than the limit, if
// any.
//
// Invariants:
// <none>
void
finishJob(JobType type);
// Runs the next appropriate waiting Job.
//
// Pre-conditions:
// A RunnableJob must exist in the JobSet
//
// Post-conditions:
// The chosen RunnableJob will have Job::doJob() called.
//
// Invariants:
// <none>
void
processTask(int instance) override;
// Returns the limit of running jobs for the given job type.
// For jobs with no limit, we return the largest int. Hopefully that
// will be enough.
int
getJobLimit(JobType type);
};
/*
An RPC command is received and is handled via ServerHandler(HTTP) or
Handler(websocket), depending on the connection type. The handler then calls
the JobQueue::postCoro() method to create a coroutine and run it at a later
point. This frees up the handler thread and allows it to continue handling
other requests while the RPC command completes its work asynchronously.
postCoro() creates a Coro object. When the Coro ctor is called, and its
coro_ member is initialized (a boost::coroutines::pull_type), execution
automatically passes to the coroutine, which we don't want at this point,
since we are still in the handler thread context. It's important to note
here that construction of a boost pull_type automatically passes execution to
the coroutine. A pull_type object automatically generates a push_type that is
passed as a parameter (do_yield) in the signature of the function the
pull_type was created with. This function is immediately called during coro_
construction and within it, Coro::yield_ is assigned the push_type
parameter (do_yield) address and called (yield()) so we can return execution
back to the caller's stack.
postCoro() then calls Coro::post(), which schedules a job on the job
queue to continue execution of the coroutine in a JobQueue worker thread at
some later time. When the job runs, we lock on the Coro::mutex_ and call
coro_ which continues where we had left off. Since we the last thing we did
in coro_ was call yield(), the next thing we continue with is calling the
function param f, that was passed into Coro ctor. It is within this
function body that the caller specifies what he would like to do while
running in the coroutine and allow them to suspend and resume execution.
A task that relies on other events to complete, such as path finding, calls
Coro::yield() to suspend its execution while waiting on those events to
complete and continue when signaled via the Coro::post() method.
There is a potential race condition that exists here where post() can get
called before yield() after f is called. Technically the problem only occurs
if the job that post() scheduled is executed before yield() is called.
If the post() job were to be executed before yield(), undefined behavior
would occur. The lock ensures that coro_ is not called again until we exit
the coroutine. At which point a scheduled resume() job waiting on the lock
would gain entry, harmlessly call coro_ and immediately return as we have
already completed the coroutine.
The race condition occurs as follows:
1- The coroutine is running.
2- The coroutine is about to suspend, but before it can do so, it must
arrange for some event to wake it up.
3- The coroutine arranges for some event to wake it up.
4- Before the coroutine can suspend, that event occurs and the
resumption of the coroutine is scheduled on the job queue. 5- Again, before
the coroutine can suspend, the resumption of the coroutine is dispatched. 6-
Again, before the coroutine can suspend, the resumption code runs the
coroutine.
The coroutine is now running in two threads.
The lock prevents this from happening as step 6 will block until the
lock is released which only happens after the coroutine completes.
*/
} // namespace xrpl
#include <xrpl/core/Coro.ipp>
namespace xrpl {
template <class F>
std::shared_ptr<JobQueue::Coro>
JobQueue::postCoro(JobType t, std::string const& name, F&& f)
{
/* First param is a detail type to make construction private.
Last param is the function the coroutine runs. Signature of
void(std::shared_ptr<Coro>).
*/
auto coro = std::make_shared<Coro>(
Coro_create_t{}, *this, t, name, std::forward<F>(f));
if (!coro->post())
{
// The Coro was not successfully posted. Disable it so it's destructor
// can run with no negative side effects. Then destroy it.
coro->expectEarlyExit();
coro.reset();
}
return coro;
}
} // namespace xrpl
#endif

View File

@@ -0,0 +1,88 @@
#ifndef XRPL_CORE_JOBTYPEDATA_H_INCLUDED
#define XRPL_CORE_JOBTYPEDATA_H_INCLUDED
#include <xrpl/basics/Log.h>
#include <xrpl/beast/insight/Collector.h>
#include <xrpl/core/JobTypeInfo.h>
namespace xrpl {
struct JobTypeData
{
private:
LoadMonitor m_load;
/* Support for insight */
beast::insight::Collector::ptr m_collector;
public:
/* The job category which we represent */
JobTypeInfo const& info;
/* The number of jobs waiting */
int waiting;
/* The number presently running */
int running;
/* And the number we deferred executing because of job limits */
int deferred;
/* Notification callbacks */
beast::insight::Event dequeue;
beast::insight::Event execute;
JobTypeData(
JobTypeInfo const& info_,
beast::insight::Collector::ptr const& collector,
Logs& logs) noexcept
: m_load(logs.journal("LoadMonitor"))
, m_collector(collector)
, info(info_)
, waiting(0)
, running(0)
, deferred(0)
{
m_load.setTargetLatency(
info.getAverageLatency(), info.getPeakLatency());
if (!info.special())
{
dequeue = m_collector->make_event(info.name() + "_q");
execute = m_collector->make_event(info.name());
}
}
/* Not copy-constructible or assignable */
JobTypeData(JobTypeData const& other) = delete;
JobTypeData&
operator=(JobTypeData const& other) = delete;
std::string
name() const
{
return info.name();
}
JobType
type() const
{
return info.type();
}
LoadMonitor&
load()
{
return m_load;
}
LoadMonitor::Stats
stats()
{
return m_load.getStats();
}
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,83 @@
#ifndef XRPL_CORE_JOBTYPEINFO_H_INCLUDED
#define XRPL_CORE_JOBTYPEINFO_H_INCLUDED
#include <xrpl/core/Job.h>
namespace xrpl {
/** Holds all the 'static' information about a job, which does not change */
class JobTypeInfo
{
private:
JobType const m_type;
std::string const m_name;
/** The limit on the number of running jobs for this job type.
A limit of 0 marks this as a "special job" which is not
dispatched via the job queue.
*/
int const m_limit;
/** Average and peak latencies for this job type. 0 is none specified */
std::chrono::milliseconds const m_avgLatency;
std::chrono::milliseconds const m_peakLatency;
public:
// Not default constructible
JobTypeInfo() = delete;
JobTypeInfo(
JobType type,
std::string name,
int limit,
std::chrono::milliseconds avgLatency,
std::chrono::milliseconds peakLatency)
: m_type(type)
, m_name(std::move(name))
, m_limit(limit)
, m_avgLatency(avgLatency)
, m_peakLatency(peakLatency)
{
}
JobType
type() const
{
return m_type;
}
std::string const&
name() const
{
return m_name;
}
int
limit() const
{
return m_limit;
}
bool
special() const
{
return m_limit == 0;
}
std::chrono::milliseconds
getAverageLatency() const
{
return m_avgLatency;
}
std::chrono::milliseconds
getPeakLatency() const
{
return m_peakLatency;
}
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,175 @@
#ifndef XRPL_CORE_JOBTYPES_H_INCLUDED
#define XRPL_CORE_JOBTYPES_H_INCLUDED
#include <xrpl/core/Job.h>
#include <xrpl/core/JobTypeInfo.h>
#include <map>
#include <string>
namespace xrpl {
class JobTypes
{
public:
using Map = std::map<JobType, JobTypeInfo>;
using const_iterator = Map::const_iterator;
private:
JobTypes()
: m_unknown(
jtINVALID,
"invalid",
0,
std::chrono::milliseconds{0},
std::chrono::milliseconds{0})
{
using namespace std::chrono_literals;
int maxLimit = std::numeric_limits<int>::max();
auto add = [this](
JobType jt,
std::string name,
int limit,
std::chrono::milliseconds avgLatency,
std::chrono::milliseconds peakLatency) {
XRPL_ASSERT(
m_map.find(jt) == m_map.end(),
"xrpl::JobTypes::JobTypes::add : unique job type input");
[[maybe_unused]] auto const inserted =
m_map
.emplace(
std::piecewise_construct,
std::forward_as_tuple(jt),
std::forward_as_tuple(
jt, name, limit, avgLatency, peakLatency))
.second;
XRPL_ASSERT(
inserted == true,
"xrpl::JobTypes::JobTypes::add : input is inserted");
};
// clang-format off
// avg peak
// JobType name limit latency latency
add(jtPACK, "makeFetchPack", 1, 0ms, 0ms);
add(jtPUBOLDLEDGER, "publishAcqLedger", 2, 10000ms, 15000ms);
add(jtVALIDATION_ut, "untrustedValidation", maxLimit, 2000ms, 5000ms);
add(jtMANIFEST, "manifest", maxLimit, 2000ms, 5000ms);
add(jtTRANSACTION_l, "localTransaction", maxLimit, 100ms, 500ms);
add(jtREPLAY_REQ, "ledgerReplayRequest", 10, 250ms, 1000ms);
add(jtLEDGER_REQ, "ledgerRequest", 3, 0ms, 0ms);
add(jtPROPOSAL_ut, "untrustedProposal", maxLimit, 500ms, 1250ms);
add(jtREPLAY_TASK, "ledgerReplayTask", maxLimit, 0ms, 0ms);
add(jtLEDGER_DATA, "ledgerData", 3, 0ms, 0ms);
add(jtCLIENT, "clientCommand", maxLimit, 2000ms, 5000ms);
add(jtCLIENT_SUBSCRIBE, "clientSubscribe", maxLimit, 2000ms, 5000ms);
add(jtCLIENT_FEE_CHANGE, "clientFeeChange", maxLimit, 2000ms, 5000ms);
add(jtCLIENT_CONSENSUS, "clientConsensus", maxLimit, 2000ms, 5000ms);
add(jtCLIENT_ACCT_HIST, "clientAccountHistory", maxLimit, 2000ms, 5000ms);
add(jtCLIENT_RPC, "clientRPC", maxLimit, 2000ms, 5000ms);
add(jtCLIENT_WEBSOCKET, "clientWebsocket", maxLimit, 2000ms, 5000ms);
add(jtRPC, "RPC", maxLimit, 0ms, 0ms);
add(jtUPDATE_PF, "updatePaths", 1, 0ms, 0ms);
add(jtTRANSACTION, "transaction", maxLimit, 250ms, 1000ms);
add(jtBATCH, "batch", maxLimit, 250ms, 1000ms);
add(jtADVANCE, "advanceLedger", maxLimit, 0ms, 0ms);
add(jtPUBLEDGER, "publishNewLedger", maxLimit, 3000ms, 4500ms);
add(jtTXN_DATA, "fetchTxnData", 5, 0ms, 0ms);
add(jtWAL, "writeAhead", maxLimit, 1000ms, 2500ms);
add(jtVALIDATION_t, "trustedValidation", maxLimit, 500ms, 1500ms);
add(jtWRITE, "writeObjects", maxLimit, 1750ms, 2500ms);
add(jtACCEPT, "acceptLedger", maxLimit, 0ms, 0ms);
add(jtPROPOSAL_t, "trustedProposal", maxLimit, 100ms, 500ms);
add(jtSWEEP, "sweep", 1, 0ms, 0ms);
add(jtNETOP_CLUSTER, "clusterReport", 1, 9999ms, 9999ms);
add(jtNETOP_TIMER, "heartbeat", 1, 999ms, 999ms);
add(jtADMIN, "administration", maxLimit, 0ms, 0ms);
add(jtMISSING_TXN, "handleHaveTransactions", 1200, 0ms, 0ms);
add(jtREQUESTED_TXN, "doTransactions", 1200, 0ms, 0ms);
add(jtPEER, "peerCommand", 0, 200ms, 2500ms);
add(jtDISK, "diskAccess", 0, 500ms, 1000ms);
add(jtTXN_PROC, "processTransaction", 0, 0ms, 0ms);
add(jtOB_SETUP, "orderBookSetup", 0, 0ms, 0ms);
add(jtPATH_FIND, "pathFind", 0, 0ms, 0ms);
add(jtHO_READ, "nodeRead", 0, 0ms, 0ms);
add(jtHO_WRITE, "nodeWrite", 0, 0ms, 0ms);
add(jtGENERIC, "generic", 0, 0ms, 0ms);
add(jtNS_SYNC_READ, "SyncReadNode", 0, 0ms, 0ms);
add(jtNS_ASYNC_READ, "AsyncReadNode", 0, 0ms, 0ms);
add(jtNS_WRITE, "WriteNode", 0, 0ms, 0ms);
// clang-format on
}
public:
static JobTypes const&
instance()
{
static JobTypes const types;
return types;
}
static std::string const&
name(JobType jt)
{
return instance().get(jt).name();
}
JobTypeInfo const&
get(JobType jt) const
{
Map::const_iterator const iter(m_map.find(jt));
XRPL_ASSERT(iter != m_map.end(), "xrpl::JobTypes::get : valid input");
if (iter != m_map.end())
return iter->second;
return m_unknown;
}
JobTypeInfo const&
getInvalid() const
{
return m_unknown;
}
Map::size_type
size() const
{
return m_map.size();
}
const_iterator
begin() const
{
return m_map.cbegin();
}
const_iterator
cbegin() const
{
return m_map.cbegin();
}
const_iterator
end() const
{
return m_map.cend();
}
const_iterator
cend() const
{
return m_map.cend();
}
JobTypeInfo m_unknown;
Map m_map;
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,70 @@
#ifndef XRPL_CORE_LOADEVENT_H_INCLUDED
#define XRPL_CORE_LOADEVENT_H_INCLUDED
#include <chrono>
#include <string>
namespace xrpl {
class LoadMonitor;
// VFALCO NOTE What is the difference between a LoadEvent and a LoadMonitor?
// VFALCO TODO Rename LoadEvent to ScopedLoadSample
//
// This looks like a scoped elapsed time measuring class
//
class LoadEvent
{
public:
// VFALCO TODO remove the dependency on LoadMonitor. Is that possible?
LoadEvent(LoadMonitor& monitor, std::string const& name, bool shouldStart);
LoadEvent(LoadEvent const&) = delete;
~LoadEvent();
std::string const&
name() const;
// The time spent waiting.
std::chrono::steady_clock::duration
waitTime() const;
// The time spent running.
std::chrono::steady_clock::duration
runTime() const;
void
setName(std::string const& name);
// Start the measurement. If already started, then
// restart, assigning the elapsed time to the "waiting"
// state.
void
start();
// Stop the measurement and report the results. The
// time reported is measured from the last call to
// start.
void
stop();
private:
LoadMonitor& monitor_;
// Represents our current state
bool running_;
// The name associated with this event, if any.
std::string name_;
// Represents the time we last transitioned states
std::chrono::steady_clock::time_point mark_;
// The time we spent waiting and running respectively
std::chrono::steady_clock::duration timeWaiting_;
std::chrono::steady_clock::duration timeRunning_;
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,72 @@
#ifndef XRPL_CORE_LOADMONITOR_H_INCLUDED
#define XRPL_CORE_LOADMONITOR_H_INCLUDED
#include <xrpl/basics/UptimeClock.h>
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/core/LoadEvent.h>
#include <chrono>
#include <mutex>
namespace xrpl {
// Monitors load levels and response times
// VFALCO TODO Rename this. Having both LoadManager and LoadMonitor is
// confusing.
//
class LoadMonitor
{
public:
explicit LoadMonitor(beast::Journal j);
void
addLoadSample(LoadEvent const& sample);
void
addSamples(int count, std::chrono::milliseconds latency);
void
setTargetLatency(
std::chrono::milliseconds avg,
std::chrono::milliseconds pk);
bool
isOverTarget(std::chrono::milliseconds avg, std::chrono::milliseconds peak);
// VFALCO TODO make this return the values in a struct.
struct Stats
{
Stats();
std::uint64_t count;
std::chrono::milliseconds latencyAvg;
std::chrono::milliseconds latencyPeak;
bool isOverloaded;
};
Stats
getStats();
bool
isOver();
private:
void
update();
std::mutex mutex_;
std::uint64_t mCounts;
int mLatencyEvents;
std::chrono::milliseconds mLatencyMSAvg;
std::chrono::milliseconds mLatencyMSPeak;
std::chrono::milliseconds mTargetLatencyAvg;
std::chrono::milliseconds mTargetLatencyPk;
UptimeClock::time_point mLastUpdate;
beast::Journal const j_;
};
} // namespace xrpl
#endif

192
include/xrpl/core/PerfLog.h Normal file
View File

@@ -0,0 +1,192 @@
#ifndef XRPL_CORE_PERFLOG_H
#define XRPL_CORE_PERFLOG_H
#include <xrpl/basics/BasicConfig.h>
#include <xrpl/core/JobTypes.h>
#include <xrpl/json/json_value.h>
#include <boost/filesystem.hpp>
#include <chrono>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
namespace beast {
class Journal;
}
namespace xrpl {
class Application;
namespace perf {
/**
* Singleton class that maintains performance counters and optionally
* writes Json-formatted data to a distinct log. It should exist prior
* to other objects launched by Application to make it accessible for
* performance logging.
*/
class PerfLog
{
public:
using steady_clock = std::chrono::steady_clock;
using system_clock = std::chrono::system_clock;
using steady_time_point = std::chrono::time_point<steady_clock>;
using system_time_point = std::chrono::time_point<system_clock>;
using seconds = std::chrono::seconds;
using milliseconds = std::chrono::milliseconds;
using microseconds = std::chrono::microseconds;
/**
* Configuration from [perf] section of rippled.cfg.
*/
struct Setup
{
boost::filesystem::path perfLog;
// log_interval is in milliseconds to support faster testing.
milliseconds logInterval{seconds(1)};
};
virtual ~PerfLog() = default;
virtual void
start()
{
}
virtual void
stop()
{
}
/**
* Log start of RPC call.
*
* @param method RPC command
* @param requestId Unique identifier to track command
*/
virtual void
rpcStart(std::string const& method, std::uint64_t requestId) = 0;
/**
* Log successful finish of RPC call
*
* @param method RPC command
* @param requestId Unique identifier to track command
*/
virtual void
rpcFinish(std::string const& method, std::uint64_t requestId) = 0;
/**
* Log errored RPC call
*
* @param method RPC command
* @param requestId Unique identifier to track command
*/
virtual void
rpcError(std::string const& method, std::uint64_t requestId) = 0;
/**
* Log queued job
*
* @param type Job type
*/
virtual void
jobQueue(JobType const type) = 0;
/**
* Log job executing
*
* @param type Job type
* @param dur Duration enqueued in microseconds
* @param startTime Time that execution began
* @param instance JobQueue worker thread instance
*/
virtual void
jobStart(
JobType const type,
microseconds dur,
steady_time_point startTime,
int instance) = 0;
/**
* Log job finishing
*
* @param type Job type
* @param dur Duration running in microseconds
* @param instance Jobqueue worker thread instance
*/
virtual void
jobFinish(JobType const type, microseconds dur, int instance) = 0;
/**
* Render performance counters in Json
*
* @return Counters Json object
*/
virtual Json::Value
countersJson() const = 0;
/**
* Render currently executing jobs and RPC calls and durations in Json
*
* @return Current executing jobs and RPC calls and durations
*/
virtual Json::Value
currentJson() const = 0;
/**
* Ensure enough room to store each currently executing job
*
* @param resize Number of JobQueue worker threads
*/
virtual void
resizeJobs(int const resize) = 0;
/**
* Rotate perf log file
*/
virtual void
rotate() = 0;
};
PerfLog::Setup
setup_PerfLog(Section const& section, boost::filesystem::path const& configDir);
std::unique_ptr<PerfLog>
make_PerfLog(
PerfLog::Setup const& setup,
Application& app,
beast::Journal journal,
std::function<void()>&& signalStop);
template <typename Func, class Rep, class Period>
auto
measureDurationAndLog(
Func&& func,
std::string const& actionDescription,
std::chrono::duration<Rep, Period> maxDelay,
beast::Journal const& journal)
{
auto start_time = std::chrono::high_resolution_clock::now();
auto result = func();
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
end_time - start_time);
if (duration > maxDelay)
{
JLOG(journal.warn())
<< actionDescription << " took " << duration.count() << " ms";
}
return result;
}
} // namespace perf
} // namespace xrpl
#endif // XRPL_CORE_PERFLOG_H

View File

@@ -0,0 +1,219 @@
#ifndef XRPL_CORE_WORKERS_H_INCLUDED
#define XRPL_CORE_WORKERS_H_INCLUDED
#include <xrpl/beast/core/LockFreeStack.h>
#include <xrpl/core/detail/semaphore.h>
#include <atomic>
#include <condition_variable>
#include <mutex>
#include <string>
#include <thread>
namespace xrpl {
namespace perf {
class PerfLog;
}
/**
* `Workers` is effectively a thread pool. The constructor takes a "callback"
* that has a `void processTask(int instance)` method, and a number of
* workers. It creates that many `Worker`s and then waits for calls to
* `Workers::addTask()`. It holds a semaphore that counts the number of
* pending "tasks", and a condition variable for the event when the last
* worker pauses itself.
*
* A "task" is just a call to the callback's `processTask` method.
* "Adding a task" means calling that method now, or remembering to call it in
* the future.
* This is implemented with a semaphore.
* If there are any workers waiting when a task is added, then one will be
* woken to claim the task.
* If not, then the next worker to wait on the semaphore will claim the task.
*
* Creating a `Worker` creates a thread that calls `Worker::run()`. When that
* thread enters `Worker::run`, it increments the count of active workers in
* the parent `Workers` object and then tries to claim a task, which blocks if
* there are none pending.
* It will be unblocked whenever the semaphore is notified (i.e. when the
* number of pending tasks is incremented).
* That only happens in two circumstances: (1) when
* `Workers::addTask` is called and (2) when `Workers` wants to pause some
* workers ("pause one worker" is considered one task), which happens when
* someone wants to stop the workers or shrink the threadpool. No worker
* threads are ever destroyed until `Workers` is destroyed; it merely pauses
* workers until then.
*
* When a waiting worker is woken, it checks whether `Workers` is trying to
* pause workers. If so, it changes its status from active to paused and
* blocks on
* its own condition variable. If not, then it calls `processTask` on the
* "callback" held by `Workers`.
*
* When a paused worker is woken, it checks whether it should exit. The signal
* to exit is only set in the destructor of `Worker`, which unblocks the
* paused thread and waits for it to exit. A `Worker::run` thread checks
* whether it needs to exit only when it is woken from a pause (not when it is
* woken from waiting). This is why the destructor for `Workers` pauses all
* the workers before destroying them.
*/
class Workers
{
public:
/** Called to perform tasks as needed. */
struct Callback
{
virtual ~Callback() = default;
Callback() = default;
Callback(Callback const&) = delete;
Callback&
operator=(Callback const&) = delete;
/** Perform a task.
The call is made on a thread owned by Workers. It is important
that you only process one task from inside your callback. Each
call to addTask will result in exactly one call to processTask.
@param instance The worker thread instance.
@see Workers::addTask
*/
virtual void
processTask(int instance) = 0;
};
/** Create the object.
A number of initial threads may be optionally specified. The
default is to create one thread per CPU.
@param threadNames The name given to each created worker thread.
*/
explicit Workers(
Callback& callback,
perf::PerfLog* perfLog,
std::string const& threadNames = "Worker",
int numberOfThreads =
static_cast<int>(std::thread::hardware_concurrency()));
~Workers();
/** Retrieve the desired number of threads.
This just returns the number of active threads that were requested. If
there was a recent call to setNumberOfThreads, the actual number of
active threads may be temporarily different from what was last requested.
@note This function is not thread-safe.
*/
int
getNumberOfThreads() const noexcept;
/** Set the desired number of threads.
@note This function is not thread-safe.
*/
void
setNumberOfThreads(int numberOfThreads);
/** Pause all threads and wait until they are paused.
If a thread is processing a task it will pause as soon as the task
completes. There may still be tasks signaled even after all threads
have paused.
@note This function is not thread-safe.
*/
void
stop();
/** Add a task to be performed.
Every call to addTask will eventually result in a call to
Callback::processTask unless the Workers object is destroyed or
the number of threads is never set above zero.
@note This function is thread-safe.
*/
void
addTask();
/** Get the number of currently executing calls of Callback::processTask.
While this function is thread-safe, the value may not stay
accurate for very long. It's mainly for diagnostic purposes.
*/
int
numberOfCurrentlyRunningTasks() const noexcept;
//--------------------------------------------------------------------------
private:
struct PausedTag
{
explicit PausedTag() = default;
};
/* A Worker executes tasks on its provided thread.
These are the states:
Active: Running the task processing loop.
Idle: Active, but blocked on waiting for a task.
Paused: Blocked waiting to exit or become active.
*/
class Worker : public beast::LockFreeStack<Worker>::Node,
public beast::LockFreeStack<Worker, PausedTag>::Node
{
public:
Worker(
Workers& workers,
std::string const& threadName,
int const instance);
~Worker();
void
notify();
private:
void
run();
private:
Workers& m_workers;
std::string const threadName_;
int const instance_;
std::thread thread_;
std::mutex mutex_;
std::condition_variable wakeup_;
int wakeCount_; // how many times to un-pause
bool shouldExit_;
};
private:
static void
deleteWorkers(beast::LockFreeStack<Worker>& stack);
private:
Callback& m_callback;
perf::PerfLog* perfLog_;
std::string m_threadNames; // The name to give each thread
std::condition_variable m_cv; // signaled when all threads paused
std::mutex m_mut;
bool m_allPaused;
semaphore m_semaphore; // each pending task is 1 resource
int m_numberOfThreads; // how many we want active now
std::atomic<int> m_activeCount; // to know when all are paused
std::atomic<int> m_pauseCount; // how many threads need to pause now
std::atomic<int>
m_runningTaskCount; // how many calls to processTask() active
beast::LockFreeStack<Worker> m_everyone; // holds all created workers
beast::LockFreeStack<Worker, PausedTag>
m_paused; // holds just paused workers
};
} // namespace xrpl
#endif

View File

@@ -0,0 +1,92 @@
/**
*
* TODO: Remove xrpl::basic_semaphore (and this file) and use
* std::counting_semaphore.
*
* Background:
* - PR: https://github.com/XRPLF/rippled/pull/5512/files
* - std::counting_semaphore had a bug fixed in both GCC and Clang:
* * GCC PR 104928: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928
* * LLVM PR 79265: https://github.com/llvm/llvm-project/pull/79265
*
* GCC:
* According to GCC Bugzilla PR104928
* (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928#c15), the fix is
* scheduled for inclusion in GCC 16.0 (see comment #15, Target
* Milestone: 16.0). It is not included in GCC 14.x or earlier, and there is no
* indication that it will be backported to GCC 13.x or 14.x branches.
*
* Clang:
* The fix for is included in Clang 19.1.0+
*
* Once the minimum compiler version is updated to > GCC 16.0 or Clang 19.1.0,
* we can remove this file.
*
* WARNING: Avoid using std::counting_semaphore until the minimum compiler
* version is updated.
*/
#ifndef XRPL_CORE_SEMAPHORE_H_INCLUDED
#define XRPL_CORE_SEMAPHORE_H_INCLUDED
#include <condition_variable>
#include <mutex>
namespace xrpl {
template <class Mutex, class CondVar>
class basic_semaphore
{
private:
Mutex m_mutex;
CondVar m_cond;
std::size_t m_count;
public:
using size_type = std::size_t;
/** Create the semaphore, with an optional initial count.
If unspecified, the initial count is zero.
*/
explicit basic_semaphore(size_type count = 0) : m_count(count)
{
}
/** Increment the count and unblock one waiting thread. */
void
notify()
{
std::lock_guard lock{m_mutex};
++m_count;
m_cond.notify_one();
}
/** Block until notify is called. */
void
wait()
{
std::unique_lock lock{m_mutex};
while (m_count == 0)
m_cond.wait(lock);
--m_count;
}
/** Perform a non-blocking wait.
@return `true` If the wait would be satisfied.
*/
bool
try_wait()
{
std::lock_guard lock{m_mutex};
if (m_count == 0)
return false;
--m_count;
return true;
}
};
using semaphore = basic_semaphore<std::mutex, std::condition_variable>;
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <string>
#include <vector>
namespace ripple {
namespace xrpl {
class RFC1751
{
@@ -42,6 +42,6 @@ private:
static char const* s_dictionary[];
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <mutex>
namespace ripple {
namespace xrpl {
/** A cryptographically secure random number engine
@@ -70,6 +70,6 @@ public:
csprng_engine&
crypto_prng();
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <cstddef>
namespace ripple {
namespace xrpl {
/** Attempts to clear the given blob of memory.
@@ -22,6 +22,6 @@ namespace ripple {
void
secure_erase(void* dest, std::size_t bytes);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <xrpl/beast/utility/PropertyStream.h>
#include <xrpl/json/json_value.h>
namespace ripple {
namespace xrpl {
/** A PropertyStream::Sink which produces a Json::Value of type objectValue. */
class JsonPropertyStream : public beast::PropertyStream
@@ -66,6 +66,6 @@ protected:
add(std::string const& v) override;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -1,445 +0,0 @@
#ifndef XRPL_JSON_OBJECT_H_INCLUDED
#define XRPL_JSON_OBJECT_H_INCLUDED
#include <xrpl/json/Writer.h>
#include <memory>
namespace Json {
/**
Collection is a base class for Array and Object, classes which provide the
facade of JSON collections for the O(1) JSON writer, while still using no
heap memory and only a very small amount of stack.
From http://json.org, JSON has two types of collection: array, and object.
Everything else is a *scalar* - a number, a string, a boolean, the special
value null, or a legacy Json::Value.
Collections must write JSON "as-it-goes" in order to get the strong
performance guarantees. This puts restrictions upon API users:
1. Only one collection can be open for change at any one time.
This condition is enforced automatically and a std::logic_error thrown if
it is violated.
2. A tag may only be used once in an Object.
Some objects have many tags, so this condition might be a little
expensive. Enforcement of this condition is turned on in debug builds and
a std::logic_error is thrown when the tag is added for a second time.
Code samples:
Writer writer;
// An empty object.
{
Object::Root (writer);
}
// Outputs {}
// An object with one scalar value.
{
Object::Root root (writer);
write["hello"] = "world";
}
// Outputs {"hello":"world"}
// Same, using chaining.
{
Object::Root (writer)["hello"] = "world";
}
// Output is the same.
// Add several scalars, with chaining.
{
Object::Root (writer)
.set ("hello", "world")
.set ("flag", false)
.set ("x", 42);
}
// Outputs {"hello":"world","flag":false,"x":42}
// Add an array.
{
Object::Root root (writer);
{
auto array = root.setArray ("hands");
array.append ("left");
array.append ("right");
}
}
// Outputs {"hands":["left", "right"]}
// Same, using chaining.
{
Object::Root (writer)
.setArray ("hands")
.append ("left")
.append ("right");
}
// Output is the same.
// Add an object.
{
Object::Root root (writer);
{
auto object = root.setObject ("hands");
object["left"] = false;
object["right"] = true;
}
}
// Outputs {"hands":{"left":false,"right":true}}
// Same, using chaining.
{
Object::Root (writer)
.setObject ("hands")
.set ("left", false)
.set ("right", true);
}
}
// Outputs {"hands":{"left":false,"right":true}}
Typical ways to make mistakes and get a std::logic_error:
Writer writer;
Object::Root root (writer);
// Repeat a tag.
{
root ["hello"] = "world";
root ["hello"] = "there"; // THROWS! in a debug build.
}
// Open a subcollection, then set something else.
{
auto object = root.setObject ("foo");
root ["hello"] = "world"; // THROWS!
}
// Open two subcollections at a time.
{
auto object = root.setObject ("foo");
auto array = root.setArray ("bar"); // THROWS!!
}
For more examples, check the unit tests.
*/
class Collection
{
public:
Collection(Collection&& c) noexcept;
Collection&
operator=(Collection&& c) noexcept;
Collection() = delete;
~Collection();
protected:
// A null parent means "no parent at all".
// Writers cannot be null.
Collection(Collection* parent, Writer*);
void
checkWritable(std::string const& label);
Collection* parent_;
Writer* writer_;
bool enabled_;
};
class Array;
//------------------------------------------------------------------------------
/** Represents a JSON object being written to a Writer. */
class Object : protected Collection
{
public:
/** Object::Root is the only Collection that has a public constructor. */
class Root;
/** Set a scalar value in the Object for a key.
A JSON scalar is a single value - a number, string, boolean, nullptr or
a Json::Value.
`set()` throws an exception if this object is disabled (which means that
one of its children is enabled).
In a debug build, `set()` also throws an exception if the key has
already been set() before.
An operator[] is provided to allow writing `object["key"] = scalar;`.
*/
template <typename Scalar>
void
set(std::string const& key, Scalar const&);
void
set(std::string const& key, Json::Value const&);
// Detail class and method used to implement operator[].
class Proxy;
Proxy
operator[](std::string const& key);
Proxy
operator[](Json::StaticString const& key);
/** Make a new Object at a key and return it.
This Object is disabled until that sub-object is destroyed.
Throws an exception if this Object was already disabled.
*/
Object
setObject(std::string const& key);
/** Make a new Array at a key and return it.
This Object is disabled until that sub-array is destroyed.
Throws an exception if this Object was already disabled.
*/
Array
setArray(std::string const& key);
protected:
friend class Array;
Object(Collection* parent, Writer* w) : Collection(parent, w)
{
}
};
class Object::Root : public Object
{
public:
/** Each Object::Root must be constructed with its own unique Writer. */
Root(Writer&);
};
//------------------------------------------------------------------------------
/** Represents a JSON array being written to a Writer. */
class Array : private Collection
{
public:
/** Append a scalar to the Arrary.
Throws an exception if this array is disabled (which means that one of
its sub-collections is enabled).
*/
template <typename Scalar>
void
append(Scalar const&);
/**
Appends a Json::Value to an array.
Throws an exception if this Array was disabled.
*/
void
append(Json::Value const&);
/** Append a new Object and return it.
This Array is disabled until that sub-object is destroyed.
Throws an exception if this Array was disabled.
*/
Object
appendObject();
/** Append a new Array and return it.
This Array is disabled until that sub-array is destroyed.
Throws an exception if this Array was already disabled.
*/
Array
appendArray();
protected:
friend class Object;
Array(Collection* parent, Writer* w) : Collection(parent, w)
{
}
};
//------------------------------------------------------------------------------
// Generic accessor functions to allow Json::Value and Collection to
// interoperate.
/** Add a new subarray at a named key in a Json object. */
Json::Value&
setArray(Json::Value&, Json::StaticString const& key);
/** Add a new subarray at a named key in a Json object. */
Array
setArray(Object&, Json::StaticString const& key);
/** Add a new subobject at a named key in a Json object. */
Json::Value&
addObject(Json::Value&, Json::StaticString const& key);
/** Add a new subobject at a named key in a Json object. */
Object
addObject(Object&, Json::StaticString const& key);
/** Append a new subarray to a Json array. */
Json::Value&
appendArray(Json::Value&);
/** Append a new subarray to a Json array. */
Array
appendArray(Array&);
/** Append a new subobject to a Json object. */
Json::Value&
appendObject(Json::Value&);
/** Append a new subobject to a Json object. */
Object
appendObject(Array&);
/** Copy all the keys and values from one object into another. */
void
copyFrom(Json::Value& to, Json::Value const& from);
/** Copy all the keys and values from one object into another. */
void
copyFrom(Object& to, Json::Value const& from);
/** An Object that contains its own Writer. */
class WriterObject
{
public:
WriterObject(Output const& output)
: writer_(std::make_unique<Writer>(output))
, object_(std::make_unique<Object::Root>(*writer_))
{
}
WriterObject(WriterObject&& other) = default;
Object*
operator->()
{
return object_.get();
}
Object&
operator*()
{
return *object_;
}
private:
std::unique_ptr<Writer> writer_;
std::unique_ptr<Object::Root> object_;
};
WriterObject
stringWriterObject(std::string&);
//------------------------------------------------------------------------------
// Implementation details.
// Detail class for Object::operator[].
class Object::Proxy
{
private:
Object& object_;
std::string const key_;
public:
Proxy(Object& object, std::string const& key);
template <class T>
void
operator=(T const& t)
{
object_.set(key_, t);
// Note: This function shouldn't return *this, because it's a trap.
//
// In Json::Value, foo[jss::key] returns a reference to a
// mutable Json::Value contained _inside_ foo. But in the case of
// Json::Object, where we write once only, there isn't any such
// reference that can be returned. Returning *this would return an
// object "a level higher" than in Json::Value, leading to obscure bugs,
// particularly in generic code.
}
};
//------------------------------------------------------------------------------
template <typename Scalar>
void
Array::append(Scalar const& value)
{
checkWritable("append");
if (writer_)
writer_->append(value);
}
template <typename Scalar>
void
Object::set(std::string const& key, Scalar const& value)
{
checkWritable("set");
if (writer_)
writer_->set(key, value);
}
inline Json::Value&
setArray(Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::arrayValue);
}
inline Array
setArray(Object& json, Json::StaticString const& key)
{
return json.setArray(std::string(key));
}
inline Json::Value&
addObject(Json::Value& json, Json::StaticString const& key)
{
return (json[key] = Json::objectValue);
}
inline Object
addObject(Object& object, Json::StaticString const& key)
{
return object.setObject(std::string(key));
}
inline Json::Value&
appendArray(Json::Value& json)
{
return json.append(Json::arrayValue);
}
inline Array
appendArray(Array& json)
{
return json.appendArray();
}
inline Json::Value&
appendObject(Json::Value& json)
{
return json.append(Json::objectValue);
}
inline Object
appendObject(Array& json)
{
return json.appendObject();
}
} // namespace Json
#endif

View File

@@ -234,7 +234,7 @@ inline void
check(bool condition, std::string const& message)
{
if (!condition)
ripple::Throw<std::logic_error>(message);
xrpl::Throw<std::logic_error>(message);
}
} // namespace Json

View File

@@ -6,6 +6,6 @@
#define JSON_ASSERT_MESSAGE(condition, message) \
if (!(condition)) \
ripple::Throw<Json::error>(message);
xrpl::Throw<Json::error>(message);
#endif

View File

@@ -199,7 +199,7 @@ public:
Value(UInt value);
Value(double value);
Value(char const* value);
Value(ripple::Number const& value);
Value(xrpl::Number const& value);
/** \brief Constructs a value from a static string.
* Like other value string constructor but do not duplicate the string for
@@ -430,7 +430,7 @@ private:
};
inline Value
to_json(ripple::Number const& number)
to_json(xrpl::Number const& number)
{
return to_string(number);
}

View File

@@ -6,7 +6,7 @@
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/ReadView.h>
namespace ripple {
namespace xrpl {
enum ApplyFlags : std::uint32_t {
tapNONE = 0x00,
@@ -267,7 +267,7 @@ public:
{
// LCOV_EXCL_START
UNREACHABLE(
"ripple::ApplyView::dirAppend : only Offers are appended to "
"xrpl::ApplyView::dirAppend : only Offers are appended to "
"book directories");
// Only Offers are appended to book directories. Call dirInsert()
// instead
@@ -407,6 +407,6 @@ insertPage(
std::function<void(std::shared_ptr<SLE> const&)> const& describe);
} // namespace directory
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <xrpl/protocol/STAmount.h>
#include <xrpl/protocol/TER.h>
namespace ripple {
namespace xrpl {
/** Editable, discardable view that can build metadata for one tx.
@@ -75,6 +75,6 @@ private:
std::optional<STAmount> deliver_;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/ReadView.h>
namespace ripple {
namespace xrpl {
class BookDirs
{
@@ -89,6 +89,6 @@ private:
static beast::Journal j_;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <xrpl/basics/base_uint.h>
#include <xrpl/protocol/STLedgerEntry.h>
namespace ripple {
namespace xrpl {
using CachedSLEs = TaggedCache<uint256, SLE const>;
}

View File

@@ -8,7 +8,7 @@
#include <mutex>
#include <type_traits>
namespace ripple {
namespace xrpl {
namespace detail {
@@ -47,10 +47,10 @@ public:
return base_.open();
}
LedgerInfo const&
info() const override
LedgerHeader const&
header() const override
{
return base_.info();
return base_.header();
}
Fees const&
@@ -164,6 +164,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -11,7 +11,7 @@
#include <xrpl/protocol/STTx.h>
#include <xrpl/protocol/TER.h>
namespace ripple {
namespace xrpl {
namespace credentials {
// These function will be used by the code that use DepositPreauth / Credentials
@@ -93,6 +93,6 @@ verifyDepositPreauth(
std::shared_ptr<SLE> const& sleDst,
beast::Journal j);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <xrpl/ledger/ReadView.h>
#include <xrpl/protocol/Indexes.h>
namespace ripple {
namespace xrpl {
/** A class that simplifies iterating ledger directory pages
@@ -108,6 +108,6 @@ private:
std::vector<uint256>::const_iterator it_;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -13,7 +13,7 @@
#include <functional>
#include <utility>
namespace ripple {
namespace xrpl {
/** Open ledger construction tag.
@@ -82,7 +82,7 @@ private:
monotonic_resource_;
txs_map txs_;
Rules rules_;
LedgerInfo info_;
LedgerHeader header_;
ReadView const* base_;
detail::RawStateTable items_;
std::shared_ptr<void const> hold_;
@@ -158,7 +158,7 @@ public:
Effects:
The LedgerInfo is copied from the base.
The LedgerHeader is copied from the base.
The rules are inherited from the base.
@@ -188,8 +188,8 @@ public:
// ReadView
LedgerInfo const&
info() const override;
LedgerHeader const&
header() const override;
Fees const&
fees() const override;
@@ -252,6 +252,6 @@ public:
std::shared_ptr<Serializer const> const& metaData) override;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -8,7 +8,7 @@
#include <map>
namespace ripple {
namespace xrpl {
namespace detail {
@@ -188,6 +188,6 @@ private:
PaymentSandbox const* ps_ = nullptr;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <xrpl/protocol/STLedgerEntry.h>
#include <xrpl/protocol/Serializer.h>
namespace ripple {
namespace xrpl {
/** Interface for ledger entry changes.
@@ -87,6 +87,6 @@ public:
std::shared_ptr<Serializer const> const& metaData) = 0;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -18,7 +18,7 @@
#include <optional>
#include <unordered_set>
namespace ripple {
namespace xrpl {
//------------------------------------------------------------------------------
@@ -80,8 +80,8 @@ public:
}
/** Returns information about the ledger. */
virtual LedgerInfo const&
info() const = 0;
virtual LedgerHeader const&
header() const = 0;
/** Returns true if this reflects an open ledger. */
virtual bool
@@ -91,14 +91,14 @@ public:
NetClock::time_point
parentCloseTime() const
{
return info().parentCloseTime;
return header().parentCloseTime;
}
/** Returns the sequence number of the base ledger. */
LedgerIndex
seq() const
{
return info().seq;
return header().seq;
}
/** Returns the fees for the base ledger. */
@@ -258,7 +258,7 @@ makeRulesGivenLedger(
DigestAwareReadView const& ledger,
std::unordered_set<uint256, beast::uhash<>> const& presets);
} // namespace ripple
} // namespace xrpl
#include <xrpl/ledger/detail/ReadViewFwdRange.ipp>

View File

@@ -4,7 +4,7 @@
#include <xrpl/ledger/RawView.h>
#include <xrpl/ledger/detail/ApplyViewBase.h>
namespace ripple {
namespace xrpl {
/** Discardable, editable view to a ledger.
@@ -39,6 +39,6 @@ public:
}
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -20,7 +20,7 @@
#include <map>
#include <utility>
namespace ripple {
namespace xrpl {
enum class WaiveTransferFee : bool { No = false, Yes };
enum class SkipEntry : bool { No = false, Yes };
@@ -1197,6 +1197,6 @@ sharesToAssetsWithdraw(
bool
after(NetClock::time_point now, std::uint32_t mark);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -11,7 +11,7 @@
#include <memory>
namespace ripple {
namespace xrpl {
namespace detail {
// Helper class that buffers modifications
@@ -139,6 +139,6 @@ private:
};
} // namespace detail
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -6,7 +6,7 @@
#include <xrpl/ledger/detail/ApplyStateTable.h>
#include <xrpl/protocol/XRPAmount.h>
namespace ripple {
namespace xrpl {
namespace detail {
class ApplyViewBase : public ApplyView, public RawView
@@ -27,8 +27,8 @@ public:
bool
open() const override;
LedgerInfo const&
info() const override;
LedgerHeader const&
header() const override;
Fees const&
fees() const override;
@@ -106,6 +106,6 @@ protected:
};
} // namespace detail
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -10,7 +10,7 @@
#include <map>
#include <utility>
namespace ripple {
namespace xrpl {
namespace detail {
// Helper class that buffers raw modifications
@@ -118,6 +118,6 @@ private:
};
} // namespace detail
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <memory>
#include <optional>
namespace ripple {
namespace xrpl {
class ReadView;
@@ -130,6 +130,6 @@ protected:
};
} // namespace detail
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -1,7 +1,7 @@
#ifndef XRPL_LEDGER_READVIEWFWDRANGEINL_H_INCLUDED
#define XRPL_LEDGER_READVIEWFWDRANGEINL_H_INCLUDED
namespace ripple {
namespace xrpl {
namespace detail {
template <class ValueType>
@@ -63,7 +63,7 @@ ReadViewFwdRange<ValueType>::iterator::operator==(iterator const& other) const
{
XRPL_ASSERT(
view_ == other.view_,
"ripple::detail::ReadViewFwdRange::iterator::operator==(iterator) "
"xrpl::detail::ReadViewFwdRange::iterator::operator==(iterator) "
"const : input view match");
if (impl_ != nullptr && other.impl_ != nullptr)
@@ -115,6 +115,6 @@ ReadViewFwdRange<ValueType>::iterator::operator++(int) -> iterator
}
} // namespace detail
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -269,7 +269,7 @@ protected:
error_code const& ec,
size_t bytesTransferred)
{
using namespace ripple;
using namespace xrpl;
if (ec)
{

View File

@@ -12,7 +12,7 @@
#include <functional>
#include <string>
namespace ripple {
namespace xrpl {
/** Provides an asynchronous HTTP client implementation with optional SSL.
*/
@@ -75,6 +75,6 @@ public:
beast::Journal& j);
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -11,7 +11,7 @@
#include <boost/asio/ssl.hpp>
#include <boost/format.hpp>
namespace ripple {
namespace xrpl {
class HTTPClientSSLContext
{
@@ -176,6 +176,6 @@ private:
bool const verify_;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <boost/asio/ssl/context.hpp>
namespace ripple {
namespace xrpl {
/** Register default SSL certificates.
Register the system default SSL root certificates. On linux/mac,
@@ -19,6 +19,6 @@ registerSSLCerts(
boost::system::error_code&,
beast::Journal j);
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <cstdint>
namespace ripple {
namespace xrpl {
namespace NodeStore {
/** A backend used for the NodeStore.
@@ -143,6 +143,6 @@ public:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -11,7 +11,7 @@
#include <condition_variable>
namespace ripple {
namespace xrpl {
namespace NodeStore {
@@ -230,7 +230,7 @@ protected:
{
XRPL_ASSERT(
count <= sz,
"ripple::NodeStore::Database::storeStats : valid inputs");
"xrpl::NodeStore::Database::storeStats : valid inputs");
storeCount_ += count;
storeSz_ += sz;
}
@@ -291,6 +291,6 @@ private:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <xrpl/nodestore/Database.h>
namespace ripple {
namespace xrpl {
namespace NodeStore {
/* This class has two key-value store Backend objects for persisting SHAMap
@@ -39,6 +39,6 @@ public:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -3,7 +3,7 @@
#include <xrpl/nodestore/Scheduler.h>
namespace ripple {
namespace xrpl {
namespace NodeStore {
/** Simple NodeStore Scheduler that just peforms the tasks synchronously. */
@@ -21,6 +21,6 @@ public:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -8,7 +8,7 @@
#include <nudb/store.hpp>
namespace ripple {
namespace xrpl {
namespace NodeStore {
@@ -61,6 +61,6 @@ public:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -4,7 +4,7 @@
#include <xrpl/nodestore/DatabaseRotating.h>
#include <xrpl/nodestore/Factory.h>
namespace ripple {
namespace xrpl {
namespace NodeStore {
@@ -83,6 +83,6 @@ public:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -7,7 +7,7 @@
// VFALCO NOTE Intentionally not in the NodeStore namespace
namespace ripple {
namespace xrpl {
/** The types of node objects. */
enum NodeObjectType : std::uint32_t {
@@ -81,6 +81,6 @@ private:
Blob const mData;
};
} // namespace ripple
} // namespace xrpl
#endif

View File

@@ -5,7 +5,7 @@
#include <chrono>
namespace ripple {
namespace xrpl {
namespace NodeStore {
enum class FetchType { synchronous, async };
@@ -66,6 +66,6 @@ public:
};
} // namespace NodeStore
} // namespace ripple
} // namespace xrpl
#endif

Some files were not shown because too many files have changed in this diff Show More