115class SnapshotHandle
final {
117 SnapshotHandle(SnapshotHandle&& other)
noexcept
118 : record_(std::exchange(other.record_,
nullptr)), free_list_(std::exchange(other.free_list_,
nullptr)) {}
121 if (record_ !=
nullptr) {
122 UASSERT(free_list_ !=
nullptr);
123 record_->data.reset();
124 free_list_->list.Push(*record_);
129 template <
typename ,
typename Traits>
132 template <
typename ,
typename Traits>
135 explicit SnapshotHandle(impl::SnapshotRecord<T>& record, impl::SnapshotRecordFreeList<T>& free_list)
noexcept
136 : record_(&record), free_list_(&free_list) {}
138 impl::SnapshotRecord<T>* record_;
139 impl::SnapshotRecordFreeList<T>* free_list_;
153class AsyncDeleter
final {
155 ~AsyncDeleter() { wait_token_storage_.WaitForAllTokens(); }
157 template <
typename T>
158 void Delete(SnapshotHandle<T>&& handle)
noexcept {
159 if constexpr (std::is_trivially_destructible_v<T> || std::is_same_v<T, std::string>) {
160 SyncDeleter{}.Delete(std::move(handle));
165 [token = wait_token_storage_.GetToken(), handle = std::move(handle)]()
mutable {}
177 utils::impl::WaitTokenStorage wait_token_storage_;
235 explicit ReadablePtr(
const Variable<T, RcuTraits>& ptr) {
236 auto* record = ptr.current_.load();
241 lock_ = record->indicator.GetLock();
263 concurrent::impl::AsymmetricThreadFenceLight();
267 auto* new_current = ptr.current_.load(std::memory_order_seq_cst);
268 if (new_current == record) {
273 record = new_current;
276 ptr_ = &*record->data;
279 ReadablePtr(
ReadablePtr&& other)
noexcept =
default;
283 ~ReadablePtr() =
default;
285 const T* Get()
const& USERVER_IMPL_LIFETIME_BOUND {
290 const T* Get() && {
return GetOnRvalue(); }
292 const T* operator->()
const& USERVER_IMPL_LIFETIME_BOUND {
return Get(); }
293 const T* operator->() && {
return GetOnRvalue(); }
295 const T& operator*()
const& USERVER_IMPL_LIFETIME_BOUND {
return *Get(); }
296 const T& operator*() && {
return *GetOnRvalue(); }
299 const T* GetOnRvalue() {
300 static_assert(!
sizeof(T),
"Don't use temporary ReadablePtr, store it to a variable");
305 concurrent::impl::StripedReadIndicatorLock lock_;
328 explicit WritablePtr(
Variable<T, RcuTraits>& var)
331 record_(&var.EmplaceSnapshot(*var.current_.load()->data))
335 template <
typename... Args>
336 WritablePtr(
Variable<T, RcuTraits>& var, std::in_place_t, Args&&... initial_value_args)
339 record_(&var.EmplaceSnapshot(std::forward<Args>(initial_value_args)...))
344 : var_(other.var_), lock_(std::move(other.lock_)), record_(std::exchange(other.record_,
nullptr)) {}
348 var_.DeleteSnapshot(*record_);
357 var_.DoAssign(*std::exchange(record_,
nullptr), lock_);
361 T* Get() & USERVER_IMPL_LIFETIME_BOUND {
363 return &*record_->data;
366 T* Get() && {
return GetOnRvalue(); }
368 T* operator->() & USERVER_IMPL_LIFETIME_BOUND {
return Get(); }
369 T* operator->() && {
return GetOnRvalue(); }
371 T& operator*() & USERVER_IMPL_LIFETIME_BOUND {
return *Get(); }
372 T& operator*() && {
return *GetOnRvalue(); }
375 [[
noreturn]]
static T* GetOnRvalue() {
376 static_assert(!
sizeof(T),
"Don't use temporary WritablePtr, store it to a variable");
381 std::unique_lock<
typename RcuTraits::MutexType> lock_;
382 impl::SnapshotRecord<T>* record_;
437 "RcuTraits should publicly inherit from rcu::DefaultRcuTraits"
441 using MutexType =
typename RcuTraits::MutexType;
442 using DeleterType =
typename RcuTraits::DeleterType;
447 template <
typename... Args>
450 : current_(&EmplaceSnapshot(std::forward<Args>(initial_value_args)...))
460 auto* record = current_.load();
461 UASSERT_MSG(record->indicator.IsFree(),
"RCU variable is destroyed while being used");
465 retired_list_.RemoveAndDisposeIf(
466 [](impl::SnapshotRecord<T>&) {
return true; },
467 [](impl::SnapshotRecord<T>& record) {
468 UASSERT_MSG(record.indicator.IsFree(),
"RCU variable is destroyed while being used");
495 template <
typename... Args>
497 return WritablePtr<T, RcuTraits>(*
this, std::in_place, std::forward<Args>(args)...);
504 void Assign(T new_value) {
WritablePtr<T, RcuTraits>(*
this, std::in_place, std::move(new_value)).Commit(); }
509 template <
typename... Args>
511 WritablePtr<T, RcuTraits>(*
this, std::in_place, std::forward<Args>(args)...).Commit();
515 std::unique_lock lock(mutex_, std::try_to_lock);
516 if (!lock.owns_lock()) {
521 ScanRetiredList(lock);
528 void DoAssign(impl::SnapshotRecord<T>& new_snapshot, std::unique_lock<MutexType>& lock) {
532 auto*
const old_snapshot = current_.load();
533 current_.store(&new_snapshot, std::memory_order_seq_cst);
536 retired_list_.Push(*old_snapshot);
537 ScanRetiredList(lock);
540 template <
typename... Args>
541 [[nodiscard]] impl::SnapshotRecord<T>& EmplaceSnapshot(Args&&... args) {
542 auto*
const free_list_record = free_list_.list.TryPop();
543 auto& record = free_list_record ? *free_list_record : *
new impl::SnapshotRecord<T>{};
547 record.data.emplace(std::forward<Args>(args)...);
549 free_list_.list.Push(record);
556 void ScanRetiredList(std::unique_lock<MutexType>& lock)
noexcept {
558 if (retired_list_.IsEmpty()) {
562 concurrent::impl::AsymmetricThreadFenceHeavy();
564 retired_list_.RemoveAndDisposeIf(
565 [](impl::SnapshotRecord<T>& record) {
return record.indicator.IsFree(); },
566 [&](impl::SnapshotRecord<T>& record) { DeleteSnapshot(record); }
570 void DeleteSnapshot(impl::SnapshotRecord<T>& record)
noexcept {
572 noexcept(deleter_.Delete(SnapshotHandle<T>{record, free_list_})),
573 "DeleterType::Delete must be noexcept"
575 deleter_.Delete(SnapshotHandle<T>{record, free_list_});
580 impl::SnapshotRecordFreeList<T> free_list_;
581 impl::SnapshotRecordRetiredList<T> retired_list_;
584 DeleterType deleter_{};
587 std::atomic<impl::SnapshotRecord<T>*> current_;