Compare commits

...

9 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
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
3 changed files with 154 additions and 38 deletions

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

@@ -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);
}
}
};