userver: userver/utils/swappingsmart.hpp Source File
Loading...
Searching...
No Matches
swappingsmart.hpp
1#pragma once
2
3#include <atomic>
4#include <memory>
5
6USERVER_NAMESPACE_BEGIN
7
8namespace utils {
9
10// Use a faster and more reliable rcu::Variable instead of this class!
11//
12// This class helps to store pointer. Pointer stored as shared_ptr.
13// Whet you call Get, you will get copy of this shared pointer under read lock.
14// When you call Set pointer, it will be stored under writelock.
15//
16// It works slower than SwappingPtr in 5 times, but readers will always have
17// shared_ptr that they obtained, so it is impossible that data will be free'd.
18template <typename T>
19class SwappingSmart {
20public:
21 SwappingSmart() = default;
22
23 explicit SwappingSmart(const std::shared_ptr<T>& ptr) { Set(ptr); }
24
25 std::shared_ptr<T> Get() const {
26 const short index = current_.load(std::memory_order_relaxed);
27 return ptrs_[index];
28 }
29 void Set(const std::shared_ptr<T>& ptr) {
30 std::shared_ptr<T> buf = ptr;
31 // wait for write lock
32 while (write_lock_.test_and_set(std::memory_order_acquire)) {
33 }
34 // read current index
35 const short index = current_.load(std::memory_order_relaxed);
36 // get new index
37 const short new_index = 1 - index;
38 // store data in new index
39 std::swap(ptrs_[new_index], buf);
40 // enable new index
41 current_.store(new_index, std::memory_order_relaxed);
42 // unlock write lock
43 write_lock_.clear(std::memory_order_release);
44 // old value will be cleaned here, after lock
45 }
46 void Set(T&& obj) { Set(std::make_shared<T>(std::move(obj))); }
47 void Clear() {
48 Set(std::make_shared<T>());
49 Set(std::make_shared<T>());
50 }
51
52private:
53 std::atomic<short> current_{0};
54 std::atomic_flag write_lock_ ATOMIC_FLAG_INIT;
55 std::shared_ptr<T> ptrs_[2];
56};
57
58} // namespace utils
59
60USERVER_NAMESPACE_END