| #ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_RCU_SIMPLE_RCU_H_ |
| #define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_RCU_SIMPLE_RCU_H_ |
| |
| #include <atomic> |
| #include <cstddef> |
| #include <vector> |
| |
| #include "absl/status/status.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/synchronization/mutex.h" |
| |
| // This is a very minimalistic implementation of RCU. It is intended to be used |
| // for simple cases where there will only be X updates to the data and this must |
| // be passed in when creating the object. |
| |
| // We will not release objects even if there are no active readers. |
| |
| // The MOST important thing to note about this data structure is that it allows |
| // for LOCKLESS GETS. |
| |
| namespace milotic_tlbmc { |
| |
| template <typename T> |
| class SimpleRcu { |
| public: |
| SimpleRcu() = default; |
| |
| explicit SimpleRcu(T&& data, size_t max_updates) : max_updates_(max_updates) { |
| data_.reserve(max_updates + 1); |
| data_.push_back(std::move(data)); |
| current_index_.store(0, std::memory_order_release); |
| } |
| |
| absl::Status Update(T&& data) { |
| int next_index = current_index_.load(std::memory_order_acquire) + 1; |
| if (next_index > max_updates_) { |
| return absl::InternalError(absl::StrCat( |
| "We have reached the maximum number of updates: ", max_updates_)); |
| } |
| data_.push_back(std::move(data)); |
| absl::MutexLock lock(&mutex_); |
| current_index_.store(next_index, std::memory_order_release); |
| return absl::OkStatus(); |
| } |
| |
| const T* Get() const { |
| int current_index = current_index_.load(std::memory_order_acquire); |
| // Specifically in g3's vector implementation, operator[] obtains current |
| // vector size(). This can lead to a data race. We can use the iterator to |
| // directly access the value without checking the size since we can |
| // guarantee we are within the vectors size. |
| return &(*(data_.begin() + current_index)); |
| } |
| |
| private: |
| const size_t max_updates_ = 0; |
| |
| absl::Mutex mutex_; |
| std::atomic<size_t> current_index_; |
| |
| // We will ALWAYS reserve the maximum number of updates, so we can guarantee |
| // all pointers will always be valid |
| std::vector<T> data_; |
| }; |
| |
| } // namespace milotic_tlbmc |
| |
| #endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_RCU_SIMPLE_RCU_H_ |