Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding feature, Range Based Lock #6379

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libs/core/synchronization/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ set(synchronization_headers
hpx/synchronization/counting_semaphore.hpp
hpx/synchronization/detail/condition_variable.hpp
hpx/synchronization/detail/counting_semaphore.hpp
hpx/synchronization/detail/range_mutex_impl.hpp
hpx/synchronization/detail/sliding_semaphore.hpp
hpx/synchronization/event.hpp
hpx/synchronization/latch.hpp
hpx/synchronization/lock_types.hpp
hpx/synchronization/mutex.hpp
hpx/synchronization/no_mutex.hpp
hpx/synchronization/once.hpp
hpx/synchronization/range_mutex.hpp
hpx/synchronization/recursive_mutex.hpp
hpx/synchronization/shared_mutex.hpp
hpx/synchronization/sliding_semaphore.hpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// Copyright (c) 2023 Johan511
//
// SPDX-License-Identifier: BSL-1.0
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// This work is based on https://github.com/Johan511/ByteLock

#pragma once

#include <hpx/datastructures/detail/flat_map.hpp>
#include <hpx/execution_base/this_thread.hpp>

#include <atomic>
#include <cstddef>
#include <memory>
#include <utility>
#include <vector>

namespace hpx::synchronization::detail {

template <typename Mtx, template <typename> typename Guard>
class range_mutex
{
template <typename Key, typename Value>
using map_ty = hpx::detail::flat_map<Key, Value>;

Mtx mtx;
std::size_t counter = 0;
map_ty<std::size_t, std::pair<std::size_t, std::size_t>> range_map;
map_ty<std::size_t, std::shared_ptr<std::atomic_bool>> waiting;

public:
std::size_t lock(std::size_t begin, std::size_t end);
std::size_t try_lock(std::size_t begin, std::size_t end);
void unlock(std::size_t lock_id);
};

template <class Mtx, template <class> class Guard>
std::size_t range_mutex<Mtx, Guard>::lock(
std::size_t begin, std::size_t end)
{
bool localFlag = false;
std::size_t blocker_id;

std::shared_ptr<std::atomic_bool> wait_flag;

while (true)
{
{
Guard<Mtx> const lock_guard(mtx);
for (auto const& it : range_map)
{
auto [b, e] = it.second;

if ((!(e < begin)) & (!(end < b)))
{
blocker_id = it.first;
localFlag = true;
wait_flag = waiting[blocker_id];
break;
}
}
if (!localFlag)
{
++counter;
range_map[counter] = {begin, end};
waiting[counter] = std::shared_ptr<std::atomic_bool>(
new std::atomic_bool(false));
return counter;
}
localFlag = false;
}
auto pred = [&wait_flag]() noexcept { return wait_flag->load(); };
util::yield_while<true>(pred, "hpx::range_mutex::lock");
}
HPX_UNREACHABLE;
}

template <class Mtx, template <class> class Guard>
void range_mutex<Mtx, Guard>::unlock(std::size_t lock_id)
{
if (lock_id == 0)
return;
Guard const lock_guard(mtx);

range_map.erase(lock_id);

waiting[lock_id]->store(true);

waiting.erase(lock_id);
return;
}

template <class Mtx, template <class> class Guard>
std::size_t range_mutex<Mtx, Guard>::try_lock(
std::size_t begin, std::size_t end)
{
Guard const lock_guard(mtx);
for (auto const& it : range_map)
{
auto [b, e] = it.second;

if (!(e < begin) && !(end < b))
{
return 0;
}
}
range_map[++counter] = {begin, end};
return counter;
}
} // namespace hpx::synchronization::detail
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright (c) 2023 Johan511
//
// SPDX-License-Identifier: BSL-1.0
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// This work is based on https://github.com/Johan511/ByteLock

#pragma once

#include <hpx/synchronization/detail/range_mutex_impl.hpp>
#include <hpx/synchronization/spinlock.hpp>

#include <cstddef>
#include <functional>
#include <mutex>
#include <system_error>
#include <utility>

namespace hpx::synchronization {
using range_mutex = hpx::synchronization::detail::range_mutex<hpx::spinlock,
std::lock_guard>;

// Lock guards for range_mutex

template <typename RangeMutex>
class range_guard
{
std::size_t lock_id;
std::reference_wrapper<RangeMutex> mutex_ref;

public:
range_guard(RangeMutex& mtx, std::size_t begin, std::size_t end)
: lock_id(mtx.lock(begin, end))
, mutex_ref(mtx)
{
}
~range_guard()
{
mutex_ref.get().unlock(lock_id);
}

range_guard(range_guard const&) = delete;
range_guard& operator=(range_guard const&) = delete;

range_guard(range_guard&& rhs_lock)
: lock_id(rhs_lock.lock_id)
, mutex_ref(rhs_lock.mutex_ref)
{
rhs_lock.lock_id = 0;
}

range_guard& operator=(range_guard&& rhs_lock)
{
mutex_ref.get().unlock(lock_id);
mutex_ref = rhs_lock.mutex_ref;
lock_id = rhs_lock.lock_id;
rhs_lock.lock_id = 0; // invalidating rhs_lock
return *this;
}
};

template <typename RangeMutex>
class range_unique_lock
{
std::size_t lock_id;
std::reference_wrapper<RangeMutex> mutex_ref;

public:
range_unique_lock(RangeMutex& mtx, std::size_t begin, std::size_t end)
: lock_id(mtx.lock(begin, end))
, mutex_ref(mtx)
{
}
~range_unique_lock()
{
mutex_ref.get().unlock(lock_id);
}

range_unique_lock(range_unique_lock const&) = delete;
range_unique_lock& operator=(range_unique_lock const&) = delete;

range_unique_lock(range_unique_lock&& rhs_lock)
: mutex_ref(rhs_lock.mutex_ref)
, lock_id(rhs_lock.lock_id)
{
rhs_lock.lock_id = 0;
}

range_unique_lock& operator=(range_unique_lock&& rhs_lock)
{
mutex_ref.get().unlock(lock_id);
mutex_ref = rhs_lock.mutex_ref;
lock_id = rhs_lock.lock_id;
rhs_lock.lock_id = 0; // invalidating rhs_lock
return *this;
}

void lock(std::size_t begin, std::size_t end)
{
if (lock_id != 0)
{
std::error_code ec = std::make_error_code(
std::errc::resource_deadlock_would_occur);
throw std::system_error(
ec, "range_unique_lock::lock: already locked");
}
lock_id = mutex_ref.get().lock(begin, end);
Johan511 marked this conversation as resolved.
Show resolved Hide resolved
}

void try_lock(std::size_t begin, std::size_t end)
{
if (lock_id != 0)
{
std::error_code ec = std::make_error_code(
std::errc::resource_deadlock_would_occur);
throw std::system_error(
ec, "range_unique_lock::lock: already locked");
}
lock_id = mutex_ref.get().try_lock(begin, end);
}

void unlock()
{
mutex_ref.get().unlock(lock_id);
lock_id = 0;
Johan511 marked this conversation as resolved.
Show resolved Hide resolved
}

void swap(range_unique_lock& uLock)
{
std::swap(mutex_ref, uLock.mutex_ref);
std::swap(lock_id, uLock.lock_id);
}

RangeMutex* release()
{
RangeMutex* mtx = mutex_ref.get();
lock_id = 0;
return mtx;
}

operator bool() const
{
return lock_id != 0;
}

bool owns_lock() const
{
return lock_id != 0;
}

RangeMutex* mutex() const
{
return mutex_ref.get();
}
};
} // namespace hpx::synchronization
2 changes: 1 addition & 1 deletion libs/core/synchronization/tests/unit/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ if(HPX_WITH_COMPILE_ONLY_TESTS)
endif()

# ##############################################################################
set(subdirs shared_mutex)
set(subdirs shared_mutex range_mutex)

# add sub directories
foreach(subdir ${subdirs})
Expand Down
33 changes: 33 additions & 0 deletions libs/core/synchronization/tests/unit/range_mutex/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Copyright (c) 2023 Johan511

# SPDX-License-Identifier: BSL-1.0 Distributed under the Boost Software License,
# Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)

# This work is based on https://github.com/Johan511/ByteLock

set(tests range_mutex)

set(range_mutex_PARAMETERS THREADS_PER_LOCALITY 4)

set(shared_mutex1_FLAGS DEPENDENCIES PRIVATE hpx_dependencies_boost)

foreach(test ${tests})
set(sources ${test}.cpp)

source_group("Source Files" FILES ${sources})

# add example executable
add_hpx_executable(
${test}_test INTERNAL_FLAGS
SOURCES ${sources} ${${test}_FLAGS}
EXCLUDE_FROM_ALL
HPX_PREFIX ${HPX_BUILD_PREFIX}
FOLDER "Tests/Unit/Modules/Core/Synchronization/range_mutex"
)

add_hpx_unit_test(
"modules.synchronization.range_mutex" ${test} ${${test}_PARAMETERS}
)

endforeach()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright (c) 2023 Johan511
//
// SPDX-License-Identifier: BSL-1.0
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

// This work is based on https://github.com/Johan511/ByteLock

#include <hpx/hpx_main.hpp>
#include <hpx/synchronization/mutex.hpp>
#include <hpx/synchronization/range_mutex.hpp>

#include "range_mutex_util.hpp"

#include <cstddef>

int main()
{
hpx::synchronization::range_mutex rm;
{
hpx::synchronization::range_unique_lock<
hpx::synchronization::range_mutex>
lg(rm, 1, 2);
}
hpx::ranged_lock::test::util::test_lock_n_times<
hpx::synchronization::range_mutex>(
10, 1'00'000, 4, 100,
[](std::size_t x, std::size_t len) { return (std::min)(x + 100, len); },
[](auto& v, std::size_t begin, std::size_t end) {
for (std::size_t i = begin; i != end; i++)
{
v[i] += 1;
}
});
return 0;
}
Loading
Loading