Skip to content

Commit

Permalink
Merge d927e99 into 76bcb86
Browse files Browse the repository at this point in the history
  • Loading branch information
Johan511 authored Feb 7, 2024
2 parents 76bcb86 + d927e99 commit 6834baa
Show file tree
Hide file tree
Showing 7 changed files with 482 additions and 1 deletion.
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
157 changes: 157 additions & 0 deletions libs/core/synchronization/include/hpx/synchronization/range_mutex.hpp
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)
{
mutex_ref = rhs_lock.mutex_ref;
lock_id = rhs_lock.lock_id;
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, "unique_lock::lock: already locked");
}
lock_id = mutex_ref.get().lock(begin, end);
}

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, "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;
}

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()
34 changes: 34 additions & 0 deletions libs/core/synchronization/tests/unit/range_mutex/range_mutex.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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);
lg.lock(3, 4);
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

0 comments on commit 6834baa

Please sign in to comment.