Compare commits

...

13 Commits

Author SHA1 Message Date
JCW
bf4dc342c6 Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-10-29 14:36:18 +00:00
JCW
a71cd5d271 Address PR comments
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-10-28 11:31:45 +00:00
JCW
45baf7339c Merge remote-tracking branch 'origin/develop' into a1q123456/remove-const-cast-from-tagged-cache 2025-10-28 11:26:58 +00:00
Shawn Xie
f4f7618173 Change fixMPTDeliveredAmount to Supported::yes (#5833)
Co-authored-by: Bart Thomee <11445373+bthomee@users.noreply.github.com>
2025-10-27 19:04:14 +00:00
Ayaz Salikhov
66f16469f9 fix: Upload all test binaries (#5932) 2025-10-27 17:27:56 +00:00
Ayaz Salikhov
1845b1c656 chore: Better pre-commit failure message (#5940) 2025-10-27 14:43:45 +00:00
Ayaz Salikhov
e192ffe964 fix: Clean up build profile options (#5934)
The `-Wno-missing-template-arg-list-after-template-kw` flag is only needed for the grpc library. Use `+=` for the default build flags to make it easier to extend in the future.

Co-authored-by: Bart Thomee <11445373+bthomee@users.noreply.github.com>
2025-10-24 15:16:15 +00:00
JCW
2bc2930a28 Fix errors
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-18 20:16:36 +01:00
JCW
e837171f7c Fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-18 20:16:36 +01:00
JCW
6f05bd035c Add test case and improve test coverage
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-18 20:16:36 +01:00
JCW
b98b42bbec Fix comment and fix formatting
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-18 20:16:36 +01:00
JCW
6e6ea4311b Add unittest
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-09-18 20:16:36 +01:00
JCW
f2271305e5 Fix attempt 2025-09-18 20:16:34 +01:00
9 changed files with 196 additions and 47 deletions

View File

@@ -9,7 +9,7 @@ on:
jobs:
# Call the workflow in the XRPLF/actions repo that runs the pre-commit hooks.
run-hooks:
uses: XRPLF/actions/.github/workflows/pre-commit.yml@a8d7472b450eb53a1e5228f64552e5974457a21a
uses: XRPLF/actions/.github/workflows/pre-commit.yml@34790936fae4c6c751f62ec8c06696f9c1a5753a
with:
runs_on: ubuntu-latest
container: '{ "image": "ghcr.io/xrplf/ci/tools-rippled-pre-commit:sha-a8c7be1" }'

View File

@@ -101,11 +101,27 @@ jobs:
--parallel $(nproc) \
--target "${CMAKE_TARGET}"
- name: Put built binaries in one location
shell: bash
working-directory: ${{ inputs.build_dir }}
env:
BUILD_TYPE_DIR: ${{ runner.os == 'Windows' && inputs.build_type || '' }}
CMAKE_TARGET: ${{ inputs.cmake_target }}
run: |
mkdir -p ./binaries/doctest/
cp ./${BUILD_TYPE_DIR}/rippled* ./binaries/
if [ "${CMAKE_TARGET}" != 'coverage' ]; then
cp ./src/tests/libxrpl/${BUILD_TYPE_DIR}/xrpl.test.* ./binaries/doctest/
fi
- name: Upload rippled artifact
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
env:
BUILD_DIR: ${{ inputs.build_dir }}
with:
name: rippled-${{ inputs.config_name }}
path: ${{ inputs.build_dir }}/${{ runner.os == 'Windows' && inputs.build_type || '' }}/rippled${{ runner.os == 'Windows' && '.exe' || '' }}
path: ${{ env.BUILD_DIR }}/binaries/
retention-days: 3
if-no-files-found: error

View File

@@ -33,6 +33,10 @@ jobs:
container: ${{ inputs.image != '' && inputs.image || null }}
timeout-minutes: 30
steps:
- name: Cleanup workspace
if: ${{ runner.os == 'macOS' }}
uses: XRPLF/actions/.github/actions/cleanup-workspace@3f044c7478548e3c32ff68980eeb36ece02b364e
- name: Download rippled artifact
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
@@ -62,9 +66,22 @@ jobs:
run: |
./rippled --version | grep libvoidstar
- name: Test the binary
- name: Run the embedded tests
if: ${{ inputs.run_tests }}
shell: bash
run: |
./rippled --unittest --unittest-jobs $(nproc)
ctest -j $(nproc) --output-on-failure
- name: Run the separate tests
if: ${{ inputs.run_tests }}
shell: bash
run: |
for test_file in ./doctest/*; do
echo "Executing $test_file"
chmod +x "$test_file"
if [[ "${{ runner.os }}" == "Windows" && "$test_file" == "./doctest/xrpl.test.net.exe" ]]; then
echo "Skipping $test_file on Windows"
else
"$test_file"
fi
done

View File

@@ -7,7 +7,7 @@ function(xrpl_add_test name)
"${CMAKE_CURRENT_SOURCE_DIR}/${name}/*.cpp"
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.cpp"
)
add_executable(${target} EXCLUDE_FROM_ALL ${ARGN} ${sources})
add_executable(${target} ${ARGN} ${sources})
isolate_headers(
${target}

View File

@@ -21,11 +21,11 @@ compiler.libcxx={{detect_api.detect_libcxx(compiler, version, compiler_exe)}}
[conf]
{% if compiler == "clang" and compiler_version >= 19 %}
tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw']
grpc/1.50.1:tools.build:cxxflags+=['-Wno-missing-template-arg-list-after-template-kw']
{% endif %}
{% if compiler == "apple-clang" and compiler_version >= 17 %}
tools.build:cxxflags=['-Wno-missing-template-arg-list-after-template-kw']
grpc/1.50.1:tools.build:cxxflags+=['-Wno-missing-template-arg-list-after-template-kw']
{% endif %}
{% if compiler == "gcc" and compiler_version < 13 %}
tools.build:cxxflags=['-Wno-restrict']
tools.build:cxxflags+=['-Wno-restrict']
{% endif %}

View File

@@ -127,15 +127,16 @@ public:
@param key The key corresponding to the object
@param data A shared pointer to the data corresponding to the object.
@param replace Function that decides if cache should be replaced
@param replaceCallback Function that decides if cache should be replaced
@return `true` If the key already existed.
@return First item: `true` If the key already existed; Second item: The
canonicalized item.
*/
template <class R>
bool
std::pair<bool, SharedPointerType>
canonicalize(
key_type const& key,
SharedPointerType& data,
SharedPointerType const& data,
R&& replaceCallback);
bool

View File

@@ -403,7 +403,7 @@ template <
class KeyEqual,
class Mutex>
template <class R>
inline bool
inline std::pair<bool, SharedPointerType>
TaggedCache<
Key,
T,
@@ -415,11 +415,9 @@ TaggedCache<
Mutex>::
canonicalize(
key_type const& key,
SharedPointerType& data,
SharedPointerType const& data,
R&& replaceCallback)
{
// Return canonical value, store if needed, refresh in cache
// Return values: true=we had the data already
std::lock_guard lock(m_mutex);
auto cit = m_cache.find(key);
@@ -431,62 +429,49 @@ TaggedCache<
std::forward_as_tuple(key),
std::forward_as_tuple(m_clock.now(), data));
++m_cache_count;
return false;
return std::make_pair(false, data);
}
Entry& entry = cit->second;
entry.touch(m_clock.now());
auto shouldReplace = [&] {
auto replaceEntryIfNecessary = [&] {
bool shouldReplace = false;
if constexpr (std::is_invocable_r_v<bool, R>)
{
// The reason for this extra complexity is for intrusive
// strong/weak combo getting a strong is relatively expensive
// and not needed for many cases.
return replaceCallback();
shouldReplace = replaceCallback();
}
else
{
return replaceCallback(entry.ptr.getStrong());
shouldReplace = replaceCallback(entry.ptr.getStrong());
}
if (shouldReplace)
entry.ptr = data;
};
if (entry.isCached())
{
if (shouldReplace())
{
entry.ptr = data;
}
else
{
data = entry.ptr.getStrong();
}
return true;
replaceEntryIfNecessary();
return std::make_pair(true, entry.ptr.getStrong());
}
auto cachedData = entry.lock();
if (cachedData)
{
if (shouldReplace())
{
entry.ptr = data;
}
else
{
entry.ptr.convertToStrong();
data = cachedData;
}
replaceEntryIfNecessary();
entry.ptr.convertToStrong();
++m_cache_count;
return true;
return std::make_pair(true, entry.ptr.getStrong());
}
entry.ptr = data;
++m_cache_count;
return false;
return std::make_pair(false, data);
}
template <
@@ -512,8 +497,8 @@ TaggedCache<
key_type const& key,
SharedPointerType const& data)
{
return canonicalize(
key, const_cast<SharedPointerType&>(data), []() { return true; });
auto [alreadyExists, _] = canonicalize(key, data, []() { return true; });
return alreadyExists;
}
template <
@@ -537,7 +522,10 @@ TaggedCache<
Mutex>::
canonicalize_replace_client(key_type const& key, SharedPointerType& data)
{
return canonicalize(key, data, []() { return false; });
auto [alreadyExists, itemInCache] =
canonicalize(key, data, []() { return false; });
data = itemInCache;
return alreadyExists;
}
template <

View File

@@ -37,7 +37,7 @@ XRPL_FEATURE(DynamicMPT, Supported::no, VoteBehavior::DefaultNo
XRPL_FIX (TokenEscrowV1, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (DelegateV1_1, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (PriceOracleOrder, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (MPTDeliveredAmount, Supported::no, VoteBehavior::DefaultNo)
XRPL_FIX (MPTDeliveredAmount, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (AMMClawbackRounding, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FEATURE(TokenEscrow, Supported::yes, VoteBehavior::DefaultNo)
XRPL_FIX (EnforceNFTokenTrustlineV2, Supported::yes, VoteBehavior::DefaultNo)

View File

@@ -24,6 +24,8 @@
#include <xrpl/basics/chrono.h>
#include <xrpl/protocol/Protocol.h>
#include <utility>
namespace ripple {
/*
@@ -148,6 +150,131 @@ public:
BEAST_EXPECT(c.getCacheSize() == 0);
BEAST_EXPECT(c.getTrackSize() == 0);
}
{
BEAST_EXPECT(!c.insert(5, "five"));
BEAST_EXPECT(c.getCacheSize() == 1);
BEAST_EXPECT(c.size() == 1);
{
auto const p1 = c.fetch(5);
BEAST_EXPECT(p1 != nullptr);
BEAST_EXPECT(c.getCacheSize() == 1);
BEAST_EXPECT(c.size() == 1);
// Advance the clock a lot
++clock;
c.sweep();
BEAST_EXPECT(c.getCacheSize() == 0);
BEAST_EXPECT(c.size() == 1);
auto p2 = std::make_shared<std::string>("five_2");
BEAST_EXPECT(c.canonicalize_replace_cache(5, p2));
BEAST_EXPECT(c.getCacheSize() == 1);
BEAST_EXPECT(c.size() == 1);
// Make sure we get the original object
BEAST_EXPECT(p1.get() != p2.get());
BEAST_EXPECT(*p2 == "five_2");
}
++clock;
c.sweep();
BEAST_EXPECT(c.getCacheSize() == 0);
BEAST_EXPECT(c.size() == 0);
}
{
testcase("intrptr");
struct MyRefCountObject : IntrusiveRefCounts
{
std::string _data;
// Needed to support weak intrusive pointers
virtual void
partialDestructor() {};
MyRefCountObject() = default;
explicit MyRefCountObject(std::string data)
: _data(std::move(data))
{
}
explicit MyRefCountObject(char const* data) : _data(data)
{
}
MyRefCountObject&
operator=(MyRefCountObject const& other)
{
_data = other._data;
return *this;
}
bool
operator==(MyRefCountObject const& other) const
{
return _data == other._data;
}
bool
operator==(std::string const& other) const
{
return _data == other;
}
};
using IntrPtrCache = TaggedCache<
Key,
MyRefCountObject,
/*IsKeyCache*/ false,
intr_ptr::SharedWeakUnionPtr<MyRefCountObject>,
intr_ptr::SharedPtr<MyRefCountObject>>;
IntrPtrCache intrPtrCache("IntrPtrTest", 1, 1s, clock, journal);
intrPtrCache.canonicalize_replace_cache(
1, intr_ptr::make_shared<MyRefCountObject>("one"));
BEAST_EXPECT(intrPtrCache.getCacheSize() == 1);
BEAST_EXPECT(intrPtrCache.size() == 1);
{
{
intrPtrCache.canonicalize_replace_cache(
1,
intr_ptr::make_shared<MyRefCountObject>(
"one_replaced"));
auto p = intrPtrCache.fetch(1);
BEAST_EXPECT(*p == "one_replaced");
// Advance the clock a lot
++clock;
intrPtrCache.sweep();
BEAST_EXPECT(intrPtrCache.getCacheSize() == 0);
BEAST_EXPECT(intrPtrCache.size() == 1);
intrPtrCache.canonicalize_replace_cache(
1,
intr_ptr::make_shared<MyRefCountObject>(
"one_replaced_2"));
auto p2 = intrPtrCache.fetch(1);
BEAST_EXPECT(*p2 == "one_replaced_2");
intrPtrCache.del(1, true);
}
intrPtrCache.canonicalize_replace_cache(
1,
intr_ptr::make_shared<MyRefCountObject>("one_replaced_3"));
auto p3 = intrPtrCache.fetch(1);
BEAST_EXPECT(*p3 == "one_replaced_3");
}
++clock;
intrPtrCache.sweep();
BEAST_EXPECT(intrPtrCache.getCacheSize() == 0);
BEAST_EXPECT(intrPtrCache.size() == 0);
}
}
};