Skip to content

chore: Add mutex wrapper from clio#6447

Open
kuznetsss wants to merge 2 commits intoXRPLF:developfrom
kuznetsss:Add_mutex
Open

chore: Add mutex wrapper from clio#6447
kuznetsss wants to merge 2 commits intoXRPLF:developfrom
kuznetsss:Add_mutex

Conversation

@kuznetsss
Copy link
Collaborator

High Level Overview of Change

This PR adds a mutex wrapper copied from clio. This wrapper makes a mutex attached to the data it protects which improves safety and readability.

Context of Change

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Refactor (non-breaking change that only restructures code)
  • Performance (increase or change in throughput and/or latency)
  • Tests (you added tests for code that already exists, or your new feature included in this PR)
  • Documentation update
  • Chore (no impact to binary, e.g. .gitignore, formatting, dropping support for older tooling)
  • Release

API Impact

  • Public API: New feature (new methods and/or new fields)
  • Public API: Breaking change (in general, breaking changes should only impact the next api_version)
  • libxrpl change (any change that may affect libxrpl or dependents of libxrpl)
  • Peer protocol change (must be backward compatible or bump the peer protocol version)

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a new mutex+data wrapper (ported from clio) intended to bundle protected data with its mutex and provide an RAII lock object that grants scoped access to the data.

Changes:

  • Add xrpl::Mutex<T, MutexType> container that owns T and a mutex.
  • Add xrpl::Lock<T, LockType, MutexType> to hold the lock and provide *, ->, and get() accessors.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +119 to +157
/**
* @brief Make a new Mutex object with the given data
*
* @tparam Args The types of the arguments to forward to the constructor of the protected data
* @param args The arguments to forward to the constructor of the protected data
* @return The Mutex object that protects the given data
*/
template <typename... Args>
static Mutex
make(Args&&... args)
{
return Mutex{ProtectedDataType{std::forward<Args>(args)...}};
}

/**
* @brief Lock the mutex and get a lock object allowing access to the protected data
*
* @tparam LockType The type of lock to use
* @return A lock on the mutex and a reference to the protected data
*/
template <template <typename...> typename LockType = std::lock_guard>
Lock<ProtectedDataType const, LockType, MutexType>
lock() const
{
return {mutex_, data_};
}

/**
* @brief Lock the mutex and get a lock object allowing access to the protected data
*
* @tparam LockType The type of lock to use
* @return A lock on the mutex and a reference to the protected data
*/
template <template <typename...> typename LockType = std::lock_guard>
Lock<ProtectedDataType, LockType, MutexType>
lock()
{
return {mutex_, data_};
}
Copy link

Copilot AI Feb 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces a new concurrency utility but there are no accompanying unit tests. Since the basics module already has gtest coverage for similar utilities, consider adding a small test that exercises make(...), lock() (const/non-const), and access operators to catch integration/const-correctness regressions.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with Copilot. Let's add basic checks (no need to test the standard mutex itself):

  • Mutex::make(...)
  • non-const lock() and mutable access through *, ->, and get()
  • const lock() and confirming it exposes const access
  • the templated lock() path / conversion to the underlying lock type
// src/tests/libxrpl/basics/Mutex_test.cpp

#include <xrpl/basics/Mutex.hpp> 
#include <gtest/gtest.h>

#include <string>
#include <type_traits>

namespace {

struct Widget
{
    std::string name;
    int value;

    Widget(std::string n, int v) : name(n), value(v) {}

    void
    bump()
    {
        ++value;
    }
};

TEST(MutexTest, MakeAndMutableAccess)
{
    auto m = xrpl::Mutex<Widget>::make("alice", 7);

    {
        auto lock = m.lock();

        static_assert(std::is_same_v<decltype(*lock), Widget&>);
        static_assert(std::is_same_v<decltype(lock.get()), Widget&>);
        static_assert(std::is_same_v<decltype(lock.operator->()), Widget*>);

        EXPECT_EQ(lock->name, "alice");
        EXPECT_EQ((*lock).value, 7);

        lock.get().value = 8;
        lock->bump();

        EXPECT_EQ(lock->value, 9);
    }

    auto verify = m.lock();
    EXPECT_EQ(verify->value, 9);
}

TEST(MutexTest, ConstLockReturnsConstView)
{
    xrpl::Mutex<Widget> const m{Widget{"bob", 3}};

    auto lock = m.lock();

    static_assert(std::is_same_v<decltype(*lock), Widget const&>);
    static_assert(std::is_same_v<decltype(lock.get()), Widget const&>);
    static_assert(std::is_same_v<decltype(lock.operator->()), Widget const*>);

    EXPECT_EQ(lock->name, "bob");
    EXPECT_EQ(lock->value, 3);
}

TEST(MutexTest, SupportsAlternateLockType)
{
    xrpl::Mutex<Widget> m{Widget{"carol", 0}};

    {
        auto lock = m.lock<std::unique_lock>();
        std::unique_lock<std::mutex>& underlying = lock;

        EXPECT_TRUE(underlying.owns_lock());

        lock->bump();
        EXPECT_EQ(lock->value, 1);
    }
}

}  // namespace

Copilot AI review requested due to automatic review settings February 27, 2026 15:06
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@codecov
Copy link

codecov bot commented Feb 27, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.8%. Comparing base (404f35d) to head (f674215).

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff            @@
##           develop   #6447     +/-   ##
=========================================
- Coverage     79.8%   79.8%   -0.1%     
=========================================
  Files          848     848             
  Lines        67757   67757             
  Branches      7558    7579     +21     
=========================================
- Hits         54074   54039     -35     
- Misses       13683   13718     +35     

see 9 files with indirect coverage changes

Impacted file tree graph

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Collaborator

@vlntb vlntb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Let's resolve the question about the unit test, and I'm happy to approve.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants