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

use thread_local prngs with rcu #10

Merged
merged 1 commit into from
Sep 5, 2023
Merged
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: 1 addition & 1 deletion CMakeSettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"cmakeCommandArgs": "-DGAPP_CXX_FLAGS=/analyze:WX- -DGAPP_BUILD_TESTS=ON -DGAPP_BUILD_BENCHMARKS=ON -DGAPP_BUILD_EXAMPLES=ON",
"ctestCommandArgs": "--output-on-failure --schedule-random",
"codeAnalysisRuleset": "${projectDir}\\core-guidelines.ruleset",
"enableMicrosoftCodeAnalysis": false,
"enableMicrosoftCodeAnalysis": true,
"inheritEnvironments": [ "msvc_x64_x64" ]
},
{
Expand Down
31 changes: 31 additions & 0 deletions src/utility/bit.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/* Copyright (c) 2023 Krisztián Rugási. Subject to the MIT License. */

#ifndef GA_UTILITY_BIT_HPP
#define GA_UTILITY_BIT_HPP

#include "utility.hpp"
#include <climits>

namespace gapp::detail
{
template<typename T>
inline constexpr size_t bitsizeof = CHAR_BIT * sizeof(T);

template<typename T>
inline constexpr T lsb_mask = T{ 1 };

template<typename T>
inline constexpr T msb_mask = T{ 1 } << (bitsizeof<T> - 1);


template<typename T>
constexpr bool is_nth_bit_set(T value, size_t n) noexcept
{
GAPP_ASSERT(n < bitsizeof<T>);

return value & (T{ 1 } << n);
}

} // namespace gapp::detail

#endif // !GA_UTILITY_BIT_HPP
120 changes: 120 additions & 0 deletions src/utility/rcu.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* Copyright (c) 2023 Krisztián Rugási. Subject to the MIT License. */

#ifndef GA_UTILITY_RCU_HPP
#define GA_UTILITY_RCU_HPP

#include "utility.hpp"
#include <atomic>
#include <mutex>
#include <shared_mutex>
#include <vector>
#include <tuple>
#include <cstdint>
#include <cstddef>

namespace gapp::detail
{
struct default_rcu_domain_tag {};

template<typename = default_rcu_domain_tag>
class rcu_domain
{
public:
inline static void read_lock() noexcept
{
reader.epoch.store(writer_epoch.load(std::memory_order_relaxed), std::memory_order_release);
std::ignore = reader.epoch.load(std::memory_order_acquire);
}

inline static void read_unlock() noexcept
{
reader.epoch.store(NOT_READING, std::memory_order_release);
}

inline static void synchronize() noexcept
{
uint64_t current = writer_epoch.load(std::memory_order_acquire);
uint64_t target = current + 1;
writer_epoch.compare_exchange_strong(current, target, std::memory_order_acq_rel);

std::shared_lock _{ reader_list_mtx };

for (const registered_reader* reader_ : reader_list)
{
while (reader_->epoch.load(std::memory_order_acquire) < target) { GAPP_PAUSE(); }
}
}

private:
struct registered_reader
{
registered_reader() noexcept
{
std::unique_lock _{ reader_list_mtx };
reader_list.push_back(this);
}

~registered_reader() noexcept
{
std::unique_lock _{ reader_list_mtx };
std::erase(reader_list, this);
}

std::atomic<uint64_t> epoch = NOT_READING;
};

inline static constexpr uint64_t NOT_READING = std::numeric_limits<uint64_t>::max();

GAPP_API inline static std::vector<registered_reader*> reader_list;
GAPP_API inline static std::shared_mutex reader_list_mtx;

alignas(128) GAPP_API inline static constinit std::atomic<uint64_t> writer_epoch = 0;
alignas(128) inline static thread_local registered_reader reader;
};


template<typename T, typename D = default_rcu_domain_tag>
class rcu_obj
{
public:
template<typename... Args>
constexpr rcu_obj(Args&&... args) :
data_(new T(std::forward<Args>(args)...))
{}

~rcu_obj() noexcept
{
T* ptr = data_.load(std::memory_order_consume);
rcu_domain<D>::synchronize();
delete ptr;
}

template<typename U>
rcu_obj& operator=(U&& value)
{
T* new_ptr = new T(std::forward<U>(value));
T* old_ptr = data_.exchange(new_ptr, std::memory_order_acq_rel);
rcu_domain<D>::synchronize();
delete old_ptr;

return *this;
}

T& get() const noexcept
{
return *data_.load(std::memory_order_consume);
}

T& operator*() const noexcept { return get(); }
T* operator->() const noexcept { return std::addressof(get()); }

void lock() const noexcept { rcu_domain<D>::read_lock(); }
void unlock() const noexcept { rcu_domain<D>::read_unlock(); }

private:
std::atomic<T*> data_;
};

} // namespace gapp::detail

#endif // !GA_UTILITY_RCU_HPP
Loading
Loading