swift
lockfree.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: Copyright (C) 2015 swift Project Community / Contributors
2 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-swift-pilot-client-1
3 
5 
6 #ifndef SWIFT_MISC_LOCKFREE_H
7 #define SWIFT_MISC_LOCKFREE_H
8 
9 #include <memory>
10 #include <tuple>
11 #include <type_traits>
12 #include <utility>
13 
14 #include <QString>
15 #include <QThread>
16 #include <QtGlobal>
17 
18 // http://www.drdobbs.com/lock-free-data-structures/184401865
19 // http://en.cppreference.com/w/cpp/memory/shared_ptr/atomic
20 
21 namespace swift::misc
22 {
23 
24  template <typename>
25  class LockFree;
26 
30  template <typename T>
32  {
33  public:
36  const T &get() const { return *m_ptr; }
37  const T *operator->() const { return m_ptr.get(); }
38  const T &operator*() const { return *m_ptr; }
39  operator const T &() const { return *m_ptr; }
41 
43  LockFreeReader(const LockFreeReader &) = default;
44 
47 
48  private:
49  friend class LockFree<std::remove_const_t<T>>;
50 
51  LockFreeReader(std::shared_ptr<const T> ptr) : m_ptr(ptr) {}
52  std::shared_ptr<const T> m_ptr;
53  };
54 
59  template <typename T>
61  {
62  public:
65  T &get() { return *m_ptr; }
66  T *operator->() { return m_ptr.get(); }
67  T &operator*() { return *m_ptr; }
68  operator T &() { return *m_ptr; }
70 
73  {
74  *m_ptr = other;
75  return *this;
76  }
77 
79  LockFreeUniqueWriter &operator=(T &&other) noexcept(std::is_nothrow_move_assignable_v<T>)
80  {
81  *m_ptr = std::move(other);
82  return *this;
83  }
84 
90 
93  : m_old(std::move(other.m_old)), m_now(std::move(other.m_now)), m_ptr(std::move(other.m_ptr))
94  {}
95 
98  {
99  std::tie(m_old, m_now, m_ptr) =
100  std::forward_as_tuple(std::move(other.m_old), std::move(other.m_now), std::move(other.m_ptr));
101  return *this;
102  }
103 
106  {
107  if (m_ptr.use_count() == 0) { return; } // *this has been moved from
108  bool success = std::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr<const T>(m_ptr));
109  Q_ASSERT_X(success, Q_FUNC_INFO, "UniqueWriter detected simultaneous writes");
110  Q_UNUSED(success);
111  }
112 
113  private:
114  friend class LockFree<T>;
115 
116  LockFreeUniqueWriter(std::shared_ptr<const T> ptr, std::shared_ptr<const T> *now)
117  : m_old(ptr), m_now(now), m_ptr(std::make_shared<T>(*m_old))
118  {}
119  std::shared_ptr<const T> m_old;
120  std::shared_ptr<const T> *m_now;
121  std::shared_ptr<T> m_ptr;
122  };
123 
127  template <typename T>
129  {
130  public:
134  T &get() { return *m_ptr; }
135  T *operator->() { return m_ptr.get(); }
136  T &operator*() { return *m_ptr; }
137  operator T &() { return *m_ptr; }
139 
142  {
143  *m_ptr = other;
144  return *this;
145  }
146 
148  LockFreeSharedWriter &operator=(T &&other) noexcept(std::is_nothrow_move_assignable_v<T>)
149  {
150  *m_ptr = std::move(other);
151  return *this;
152  }
153 
157  bool operator!() { return !operator bool(); }
158 
162  operator bool()
163  {
164  Q_ASSERT_X(m_ptr.use_count() > 0, Q_FUNC_INFO, "SharedWriter tried to commit changes twice");
165  if (std::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr<const T>(m_ptr)))
166  {
167  m_ptr.reset();
168  return true;
169  }
170  QThread::msleep(1);
171  m_old = std::atomic_load(m_now);
172  m_ptr = std::make_shared<T>(*m_old);
173  return false;
174  }
175 
178  {
179  Q_ASSERT_X(m_ptr.use_count() == 0, Q_FUNC_INFO, "SharedWriter destroyed without committing changes");
180  }
181 
187 
190  : m_old(std::move(other.m_old)), m_now(std::move(other.m_now)), m_ptr(std::move(other.m_ptr))
191  {}
192 
195  {
196  std::tie(m_old, m_now, m_ptr) =
197  std::forward_as_tuple(std::move(other.m_old), std::move(other.m_now), std::move(other.m_ptr));
198  return *this;
199  }
200 
201  private:
202  friend class LockFree<T>;
203 
204  LockFreeSharedWriter(std::shared_ptr<const T> ptr, std::shared_ptr<const T> *now)
205  : m_old(ptr), m_now(now), m_ptr(std::make_shared<T>(*m_old))
206  {}
207  std::shared_ptr<const T> m_old;
208  std::shared_ptr<const T> *m_now;
209  std::shared_ptr<T> m_ptr;
210  };
211 
217  template <typename T>
218  class LockFree
219  {
220  public:
222  LockFree() = default;
223 
225  LockFree(const T &other) : m_ptr(std::make_shared<const T>(other)) {}
226 
228  LockFree(T &&other) noexcept(std::is_nothrow_move_assignable_v<T>)
229  : m_ptr(std::make_shared<const T>(std::move(other)))
230  {}
231 
234  LockFree(const LockFree &) = delete;
235  LockFree &operator=(const LockFree &) = delete;
236  LockFree(LockFree &&) = delete;
237  LockFree &operator=(LockFree &&) = delete;
239 
241  LockFreeReader<const T> read() const { return { std::atomic_load(&m_ptr) }; }
242 
244  LockFreeUniqueWriter<T> uniqueWrite() { return { std::atomic_load(&m_ptr), &m_ptr }; }
245 
247  LockFreeSharedWriter<T> sharedWrite() { return { std::atomic_load(&m_ptr), &m_ptr }; }
248 
250  template <typename F>
251  auto read(F &&inspector)
252  {
253  return std::forward<F>(inspector)(read().get());
254  }
255 
257  template <typename F>
258  void uniqueWrite(F &&mutator)
259  {
260  std::forward<F>(mutator)(uniqueWrite().get());
261  }
262 
265  template <typename F>
266  void sharedWrite(F &&mutator)
267  {
268  auto writer = sharedWrite();
269  do {
270  std::forward<F>(mutator)(writer.get());
271  }
272  while (!writer);
273  }
274 
275  private:
276  std::shared_ptr<const T> m_ptr = std::make_shared<const T>();
277  };
278 
282  template <template <typename> class T, typename... Ts>
284  {
285  public:
288  LockFreeMulti(std::tuple<T<Ts> &&...> &&tup) : m_tup(std::move(tup)) {}
289 
293  template <typename F>
294  auto operator()(F &&function) &&
295  {
296  return call(std::forward<F>(function), std::make_index_sequence<sizeof...(Ts)>());
297  }
298 
299  private:
300  template <typename F, size_t... Is>
301  auto call(F &&function, std::index_sequence<Is...>)
302  {
303  return std::forward<F>(function)(std::get<Is>(m_tup).get()...);
304  }
305 
306  const std::tuple<T<Ts>...> m_tup;
307  };
308 
312  template <typename... Ts>
314  {
315  return { std::forward_as_tuple(vs.read()...) };
316  }
317 
321  template <typename... Ts>
323  {
324  return { std::forward_as_tuple(vs.uniqueWrite()...) };
325  }
326 
328 
331  template <typename T>
332  typename T::const_iterator begin(const LockFreeReader<T> &reader)
333  {
334  return reader->begin();
335  }
336 
337  template <typename T>
338  typename T::const_iterator end(const LockFreeReader<T> &reader)
339  {
340  return reader->end();
341  }
342 
343  template <typename T>
344  typename T::iterator begin(const LockFreeUniqueWriter<T> &writer)
345  {
346  return writer->begin();
347  }
348 
349  template <typename T>
350  typename T::iterator end(const LockFreeUniqueWriter<T> &writer)
351  {
352  return writer->end();
353  }
354 
355  template <typename T>
356  typename T::iterator begin(const LockFreeSharedWriter<T> &writer)
357  {
358  return writer->begin();
359  }
360 
361  template <typename T>
362  typename T::iterator end(const LockFreeSharedWriter<T> &writer)
363  {
364  return writer->end();
365  }
367 
369 
374  template <typename T>
375  typename T::const_iterator begin(const LockFreeReader<T> &&) = delete;
376 
377  template <typename T>
378  typename T::const_iterator end(const LockFreeReader<T> &&) = delete;
379 
380  template <typename T>
381  typename T::iterator begin(const LockFreeUniqueWriter<T> &&) = delete;
382 
383  template <typename T>
384  typename T::iterator end(const LockFreeUniqueWriter<T> &&) = delete;
385 
386  template <typename T>
387  typename T::iterator begin(const LockFreeSharedWriter<T> &&) = delete;
388 
389  template <typename T>
390  typename T::iterator end(const LockFreeSharedWriter<T> &&) = delete;
392 
393 } // namespace swift::misc
394 
395 #endif // SWIFT_MISC_LOCKFREE_H
Lock-free wrapper for synchronizing multi-threaded access to an object.
Definition: lockfree.h:219
LockFree & operator=(const LockFree &)=delete
LockFree cannot be copied or moved.
LockFreeUniqueWriter< T > uniqueWrite()
Return an object which can write a new value, as long as there are no other writes.
Definition: lockfree.h:244
LockFree()=default
Default constructor. Object will contain a default-constructed T.
LockFree & operator=(LockFree &&)=delete
LockFree cannot be copied or moved.
void sharedWrite(F &&mutator)
Pass a modifiable reference to the functor mutator. Safe if there are multiple writers....
Definition: lockfree.h:266
auto read(F &&inspector)
Pass the current value to the functor inspector, and return whatever inspector returns.
Definition: lockfree.h:251
LockFree(LockFree &&)=delete
LockFree cannot be copied or moved.
LockFree(const T &other)
Construct by copying from a T.
Definition: lockfree.h:225
LockFreeReader< const T > read() const
Return an object which can read the current value.
Definition: lockfree.h:241
LockFree(const LockFree &)=delete
LockFree cannot be copied or moved.
LockFree(T &&other) noexcept(std::is_nothrow_move_assignable_v< T >)
Construct by moving from a T.
Definition: lockfree.h:228
LockFreeSharedWriter< T > sharedWrite()
Return an object which can write a new value, even if there are other writes.
Definition: lockfree.h:247
void uniqueWrite(F &&mutator)
Pass a modifiable reference to the functor mutator. Unsafe if there are multiple writers.
Definition: lockfree.h:258
Compose multiple LockFreeReader or LockFreeUniqueWriter instances.
Definition: lockfree.h:284
auto operator()(F &&function) &&
Function call operator.
Definition: lockfree.h:294
LockFreeMulti(std::tuple< T< Ts > &&... > &&tup)
Construct from a forwarded tuple. Prefer to construct via swift::misc::multiRead or swift::misc::mult...
Definition: lockfree.h:288
Return value of LockFree::read().
Definition: lockfree.h:32
const T & operator*() const
Return the value that was present when the reader was created.
Definition: lockfree.h:38
LockFreeReader(const LockFreeReader &)=default
Copy constructor.
LockFreeReader & operator=(const LockFreeReader &)=default
Copy assignment operator.
const T * operator->() const
Return the value that was present when the reader was created.
Definition: lockfree.h:37
const T & get() const
Return the value that was present when the reader was created.
Definition: lockfree.h:36
Return value of LockFree::sharedWrite().
Definition: lockfree.h:129
LockFreeSharedWriter & operator=(LockFreeSharedWriter &&other) noexcept
Move assignment operator.
Definition: lockfree.h:194
LockFreeSharedWriter & operator=(T &&other) noexcept(std::is_nothrow_move_assignable_v< T >)
Replace the stored value by moving from a T. The change is applied by evaluating in a bool context.
Definition: lockfree.h:148
LockFreeSharedWriter(const LockFreeSharedWriter &)=delete
LockFreeSharedWriter cannot be copied.
T & operator*()
The value can be modified through the returned reference. The modification is applied by evaluating i...
Definition: lockfree.h:136
T * operator->()
The value can be modified through the returned reference. The modification is applied by evaluating i...
Definition: lockfree.h:135
LockFreeSharedWriter(LockFreeSharedWriter &&other) noexcept
Move constructor.
Definition: lockfree.h:189
LockFreeSharedWriter & operator=(const LockFreeSharedWriter &)=delete
LockFreeSharedWriter cannot be copied.
bool operator!()
Try to overwrite the original object with the new one stored in the writer, and return false on succe...
Definition: lockfree.h:157
LockFreeSharedWriter & operator=(const T &other)
Replace the stored value by copying from a T. The change is applied by evaluating in a bool context.
Definition: lockfree.h:141
T & get()
The value can be modified through the returned reference. The modification is applied by evaluating i...
Definition: lockfree.h:134
~LockFreeSharedWriter()
Destructor. The writer's changes must be committed before this is called.
Definition: lockfree.h:177
Return value of LockFree::uniqueWrite().
Definition: lockfree.h:61
T * operator->()
The value can be modified through the returned reference. The modification is applied in the destruct...
Definition: lockfree.h:66
LockFreeUniqueWriter & operator=(const LockFreeUniqueWriter &)=delete
LockFreeUniqueWriter cannot be copied.
LockFreeUniqueWriter(LockFreeUniqueWriter &&other) noexcept
Move constructor.
Definition: lockfree.h:92
LockFreeUniqueWriter & operator=(const T &other)
Replace the stored value by copying from a T. The change is applied in the destructor.
Definition: lockfree.h:72
~LockFreeUniqueWriter()
Destructor. The original object will be overwritten by the new one stored in the writer.
Definition: lockfree.h:105
LockFreeUniqueWriter & operator=(T &&other) noexcept(std::is_nothrow_move_assignable_v< T >)
Replace the stored value by moving from a T. The change is applied in the destructor.
Definition: lockfree.h:79
T & get()
The value can be modified through the returned reference. The modification is applied in the destruct...
Definition: lockfree.h:65
LockFreeUniqueWriter(const LockFreeUniqueWriter &)=delete
LockFreeUniqueWriter cannot be copied.
LockFreeUniqueWriter & operator=(LockFreeUniqueWriter &&other) noexcept
Move assignment operator.
Definition: lockfree.h:97
T & operator*()
The value can be modified through the returned reference. The modification is applied in the destruct...
Definition: lockfree.h:67
Free functions in swift::misc.
T::const_iterator begin(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:332
LockFreeMulti< LockFreeReader, const Ts... > multiRead(const LockFree< Ts > &...vs)
Return a callable object for reading from multiple LockFree instances simultaneously.
Definition: lockfree.h:313
T::const_iterator end(const LockFreeReader< T > &reader)
Non-member begin() and end() for so LockFree containers can be used in ranged for loops.
Definition: lockfree.h:338
LockFreeMulti< LockFreeUniqueWriter, Ts... > multiUniqueWrite(LockFree< Ts > &...vs)
Return a callable object for writing to multiple LockFree instances simultaneously.
Definition: lockfree.h:322