-
-
Notifications
You must be signed in to change notification settings - Fork 442
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
482 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
libs/core/synchronization/include/hpx/synchronization/detail/range_mutex_impl.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
157
libs/core/synchronization/include/hpx/synchronization/range_mutex.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
libs/core/synchronization/tests/unit/range_mutex/CMakeLists.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
34
libs/core/synchronization/tests/unit/range_mutex/range_mutex.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} |
Oops, something went wrong.