CsLibGuarded  1.4.1
cs_rcu_guarded.h
1 /***********************************************************************
2 *
3 * Copyright (c) 2016-2024 Ansel Sermersheim
4 *
5 * This file is part of CsLibGuarded.
6 *
7 * CsLibGuarded is free software, released under the BSD 2-Clause license.
8 * For license details refer to LICENSE provided with this project.
9 *
10 * CsLibGuarded is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 *
14 * https://opensource.org/licenses/BSD-2-Clause
15 *
16 ***********************************************************************/
17 
18 #ifndef CSLIBGUARDED_RCU_GUARDED_H
19 #define CSLIBGUARDED_RCU_GUARDED_H
20 
21 #include <memory>
22 
23 namespace libguarded
24 {
25 
26 // 9 comment line(s) omitted
35 template <typename T>
36 class rcu_guarded
37 {
38  public:
39  class write_handle;
40  class read_handle;
41 
42  template <typename... Us>
43  rcu_guarded(Us &&... data);
44 
45  // write access
46  [[nodiscard]] write_handle lock_write();
47 
48  // read access
49  [[nodiscard]] read_handle lock_read() const;
50 
51  class write_handle
52  {
53  public:
54  using pointer = T *;
55  using element_type = T;
56 
57  write_handle(T *ptr);
58 
59  write_handle(const write_handle &other) = delete;
60  write_handle &operator=(const write_handle &other) = delete;
61 
62  write_handle(write_handle &&other) {
63  m_ptr = other.m_ptr;
64  m_guard = std::move(other.m_guard);
65  m_accessed = other.m_accessed;
66 
67  other.m_ptr = nullptr;
68  other.m_accessed = false;
69  }
70 
71  write_handle &operator=(write_handle &&other) {
72 
73  if (m_accessed) {
74  m_guard.rcu_write_unlock(*m_ptr);
75  }
76 
77  m_ptr = other.m_ptr;
78  m_guard = std::move(other.m_guard);
79  m_accessed = other.m_accessed;
80 
81  other.m_ptr = nullptr;
82  other.m_accessed = false;
83  }
84 
85  ~write_handle()
86  {
87  if (m_accessed) {
88  m_guard.rcu_write_unlock(*m_ptr);
89  }
90  }
91 
92  T &operator*() const {
93  access();
94  return *m_ptr;
95  }
96 
97  T *operator->() const {
98  access();
99  return m_ptr;
100  }
101 
102  private:
103  void access() const {
104  if (! m_accessed) {
105  m_guard.rcu_write_lock(*m_ptr);
106  m_accessed = true;
107  }
108  }
109 
110  T *m_ptr;
111  mutable typename T::rcu_write_guard m_guard;
112  mutable bool m_accessed;
113  };
114 
115  class read_handle
116  {
117  public:
118  using pointer = const T *;
119  using element_type = const T;
120 
121  read_handle(const T *ptr)
122  : m_ptr(ptr), m_accessed(false)
123  {
124  }
125 
126  read_handle(const read_handle &other) = delete;
127  read_handle &operator=(const read_handle &other) = delete;
128 
129  read_handle(read_handle &&other) {
130  m_ptr = other.m_ptr;
131  m_guard = std::move(other.m_guard);
132  m_accessed = other.m_accessed;
133 
134  other.m_ptr = nullptr;
135  other.m_accessed = false;
136  }
137 
138  read_handle &operator=(read_handle &&other) {
139 
140  if (m_accessed) {
141  m_guard.rcu_read_unlock(*m_ptr);
142  }
143 
144  m_ptr = other.m_ptr;
145  m_guard = std::move(other.m_guard);
146  m_accessed = other.m_accessed;
147 
148  other.m_ptr = nullptr;
149  other.m_accessed = false;
150  }
151 
152  ~read_handle()
153  {
154  if (m_accessed) {
155  m_guard.rcu_read_unlock(*m_ptr);
156  }
157  }
158 
159  const T &operator*() const {
160  access();
161  return *m_ptr;
162  }
163 
164  const T *operator->() const {
165  access();
166  return m_ptr;
167  }
168 
169  private:
170  void access() const {
171  if (! m_accessed) {
172  m_guard.rcu_read_lock(*m_ptr);
173  m_accessed = true;
174  }
175  }
176 
177  const T *m_ptr;
178  mutable typename T::rcu_read_guard m_guard;
179  mutable bool m_accessed;
180  };
181 
182  private:
183  T m_obj;
184 };
185 
186 template <typename T>
187 template <typename... Us>
188 rcu_guarded<T>::rcu_guarded(Us &&... data)
189  : m_obj(std::forward<Us>(data)...)
190 {
191 }
192 
193 template <typename T>
194 auto rcu_guarded<T>::lock_write() -> write_handle
195 {
196  return write_handle(&m_obj);
197 }
198 
199 template <typename T>
200 auto rcu_guarded<T>::lock_read() const -> read_handle
201 {
202  return read_handle(&m_obj);
203 }
204 
205 template <typename T>
206 rcu_guarded<T>::write_handle::write_handle(T *ptr)
207  : m_ptr(ptr), m_accessed(false)
208 {
209 }
210 
211 } // namespace libguarded
212 
213 #endif