Compare commits

...

2 Commits

Author SHA1 Message Date
JCW
47d46ef3ae Add unittest
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-07-31 14:39:05 +01:00
JCW
2fd8331d0a Fix attempt
Signed-off-by: JCW <a1q123456@users.noreply.github.com>
2025-07-31 13:51:35 +01:00
3 changed files with 61 additions and 6 deletions

View File

@@ -37,6 +37,28 @@
namespace ripple {
namespace detail {
template <typename T>
constexpr bool IsStatelessLambdaV = std::is_empty_v<T> && std::is_constructible_v<T>;
template<class Lambda, int=(Lambda{}(), 0)>
constexpr std::true_type IsConstexpr(Lambda);
constexpr std::false_type IsConstexpr(...);
template <typename T>
constexpr bool IsConstexprInvocableV = IsStatelessLambdaV<T> && decltype(IsConstexpr(T{})){};
template <typename Lambda, bool ConstInvocable = IsConstexprInvocableV<Lambda>>
constexpr bool ShouldTakeConstReferenceV = false;
template <typename Lambda>
constexpr bool ShouldTakeConstReferenceV<Lambda, true> = Lambda{}();
template <typename Lambda>
constexpr bool ShouldTakeConstReferenceV<Lambda, false> = true;
}
/** Map/cache combination.
This class implements a cache and a map. The cache keeps objects alive
in the map. The map allows multiple code paths that reference objects
@@ -118,6 +140,15 @@ public:
del(key_type const& key, bool valid);
public:
// We take a const reference if R (the replaceCallback) is a stateless
// lambda and can be evaluated at compile time, and it's evaluated to true,
// because there's no chance to update the parameter.
template <class R>
using SharedPointerTypeReference = std::conditional_t<
detail::ShouldTakeConstReferenceV<R>,
SharedPointerType const&,
SharedPointerType&>;
/** Replace aliased objects with originals.
Due to concurrency it is possible for two separate objects with
@@ -127,7 +158,7 @@ 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.
*/
@@ -135,7 +166,7 @@ public:
bool
canonicalize(
key_type const& key,
SharedPointerType& data,
SharedPointerTypeReference<R> data,
R&& replaceCallback);
bool

View File

@@ -415,7 +415,7 @@ TaggedCache<
Mutex>::
canonicalize(
key_type const& key,
SharedPointerType& data,
SharedPointerTypeReference<R> data,
R&& replaceCallback)
{
// Return canonical value, store if needed, refresh in cache
@@ -457,7 +457,7 @@ TaggedCache<
{
entry.ptr = data;
}
else
else if constexpr (std::assignable_from<decltype(data), decltype(entry.ptr.getStrong())>)
{
data = entry.ptr.getStrong();
}
@@ -473,7 +473,7 @@ TaggedCache<
{
entry.ptr = data;
}
else
else if constexpr (std::assignable_from<decltype(data), decltype(entry.ptr.getStrong())>)
{
entry.ptr.convertToStrong();
data = cachedData;
@@ -513,7 +513,7 @@ TaggedCache<
SharedPointerType const& data)
{
return canonicalize(
key, const_cast<SharedPointerType&>(data), []() { return true; });
key, data, []() { return true; });
}
template <

View File

@@ -148,6 +148,30 @@ 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.getTrackSize() == 1);
auto const p1 = c.fetch(5);
BEAST_EXPECT(p1 != nullptr);
BEAST_EXPECT(c.getCacheSize() == 1);
BEAST_EXPECT(c.getTrackSize() == 1);
// Advance the clock a lot
++clock;
c.sweep();
BEAST_EXPECT(c.getCacheSize() == 0);
BEAST_EXPECT(c.getTrackSize() == 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.getTrackSize() == 1);
// Make sure we get the original object
BEAST_EXPECT(p1.get() != p2.get());
BEAST_EXPECT(*p2 == "five_2");
}
}
};