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 
109 #ifdef __cpp_lib_atomic_shared_ptr // atomic shared pointer is currently not supported by clang
110  bool success = m_now->compare_exchange_strong(m_old, std::shared_ptr<const T>(m_ptr));
111 #else
112  bool success = std::atomic_compare_exchange_strong(m_now, &m_old, std::shared_ptr<const T>(m_ptr));
113 #endif
114 
115  Q_ASSERT_X(success, Q_FUNC_INFO, "UniqueWriter detected simultaneous writes");
116  Q_UNUSED(success);
117  }
118 
119  private:
120  friend class LockFree<T>;
121 
122 #ifdef __cpp_lib_atomic_shared_ptr
123  LockFreeUniqueWriter(std::shared_ptr<const T> ptr, std::atomic<std::shared_ptr<const T>> *now)
124 #else
125  LockFreeUniqueWriter(std::shared_ptr<const T> ptr, std::shared_ptr<const T> *now)
126 #endif
127  : m_old(ptr), m_now(now), m_ptr(std::make_shared<T>(*m_old))
128  {}
129  std::shared_ptr<const T> m_old;
130 #ifdef __cpp_lib_atomic_shared_ptr
131  std::atomic<std::shared_ptr<const T>> *m_now;
132 #else
133  std::shared_ptr<const T> *m_now;
134 #endif
135  std::shared_ptr<T> m_ptr;
136  };
137 
143  template <typename T>
144  class LockFree
145  {
146  public:
148  LockFree() = default;
149 
151  LockFree(const T &other) : m_ptr(std::make_shared<const T>(other)) {}
152 
154  LockFree(T &&other) noexcept(std::is_nothrow_move_assignable_v<T>)
155  : m_ptr(std::make_shared<const T>(std::move(other)))
156  {}
157 
160  LockFree(const LockFree &) = delete;
161  LockFree &operator=(const LockFree &) = delete;
162  LockFree(LockFree &&) = delete;
163  LockFree &operator=(LockFree &&) = delete;
165 
167 #ifdef __cpp_lib_atomic_shared_ptr
168  LockFreeReader<const T> read() const { return { m_ptr.load() }; }
169 #else
170  LockFreeReader<const T> read() const { return { std::atomic_load(&m_ptr) }; }
171 #endif
172 
174 #ifdef __cpp_lib_atomic_shared_ptr
175  LockFreeUniqueWriter<T> uniqueWrite() { return { m_ptr.load(), &m_ptr }; }
176 #else
177  LockFreeUniqueWriter<T> uniqueWrite() { return { std::atomic_load(&m_ptr), &m_ptr }; }
178 #endif
179 
181  template <typename F>
182  auto read(F &&inspector)
183  {
184  return std::forward<F>(inspector)(read().get());
185  }
186 
188  template <typename F>
189  void uniqueWrite(F &&mutator)
190  {
191  std::forward<F>(mutator)(uniqueWrite().get());
192  }
193 
194  private:
195 #ifdef __cpp_lib_atomic_shared_ptr
196  std::atomic<std::shared_ptr<const T>> m_ptr = std::make_shared<const T>();
197 #else
198  std::shared_ptr<const T> m_ptr = std::make_shared<const T>();
199 #endif
200  };
201 
205  template <template <typename> class T, typename... Ts>
207  {
208  public:
211  LockFreeMulti(std::tuple<T<Ts> &&...> &&tup) : m_tup(std::move(tup)) {}
212 
216  template <typename F>
217  auto operator()(F &&function) &&
218  {
219  return call(std::forward<F>(function), std::make_index_sequence<sizeof...(Ts)>());
220  }
221 
222  private:
223  template <typename F, size_t... Is>
224  auto call(F &&function, std::index_sequence<Is...>)
225  {
226  return std::forward<F>(function)(std::get<Is>(m_tup).get()...);
227  }
228 
229  const std::tuple<T<Ts>...> m_tup;
230  };
231 
235  template <typename... Ts>
237  {
238  return { std::forward_as_tuple(vs.read()...) };
239  }
240 
244  template <typename... Ts>
246  {
247  return { std::forward_as_tuple(vs.uniqueWrite()...) };
248  }
249 
251 
254  template <typename T>
255  typename T::const_iterator begin(const LockFreeReader<T> &reader)
256  {
257  return reader->begin();
258  }
259 
260  template <typename T>
261  typename T::const_iterator end(const LockFreeReader<T> &reader)
262  {
263  return reader->end();
264  }
265 
266  template <typename T>
267  typename T::iterator begin(const LockFreeUniqueWriter<T> &writer)
268  {
269  return writer->begin();
270  }
271 
272  template <typename T>
273  typename T::iterator end(const LockFreeUniqueWriter<T> &writer)
274  {
275  return writer->end();
276  }
278 
280 
285  template <typename T>
286  typename T::const_iterator begin(const LockFreeReader<T> &&) = delete;
287 
288  template <typename T>
289  typename T::const_iterator end(const LockFreeReader<T> &&) = delete;
290 
291  template <typename T>
292  typename T::iterator begin(const LockFreeUniqueWriter<T> &&) = delete;
293 
294  template <typename T>
295  typename T::iterator end(const LockFreeUniqueWriter<T> &&) = delete;
297 
298 } // namespace swift::misc
299 
300 #endif // SWIFT_MISC_LOCKFREE_H
Lock-free wrapper for synchronizing multi-threaded access to an object.
Definition: lockfree.h:145
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:177
LockFree()=default
Default constructor. Object will contain a default-constructed T.
LockFree & operator=(LockFree &&)=delete
LockFree cannot be copied or moved.
auto read(F &&inspector)
Pass the current value to the functor inspector, and return whatever inspector returns.
Definition: lockfree.h:182
LockFree(LockFree &&)=delete
LockFree cannot be copied or moved.
LockFree(const T &other)
Construct by copying from a T.
Definition: lockfree.h:151
LockFreeReader< const T > read() const
Return an object which can read the current value.
Definition: lockfree.h:170
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:154
void uniqueWrite(F &&mutator)
Pass a modifiable reference to the functor mutator. Unsafe if there are multiple writers.
Definition: lockfree.h:189
Compose multiple LockFreeReader or LockFreeUniqueWriter instances.
Definition: lockfree.h:207
auto operator()(F &&function) &&
Function call operator.
Definition: lockfree.h:217
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:211
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::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:255
LockFreeMulti< LockFreeReader, const Ts... > multiRead(const LockFree< Ts > &...vs)
Return a callable object for reading from multiple LockFree instances simultaneously.
Definition: lockfree.h:236
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:261
LockFreeMulti< LockFreeUniqueWriter, Ts... > multiUniqueWrite(LockFree< Ts > &...vs)
Return a callable object for writing to multiple LockFree instances simultaneously.
Definition: lockfree.h:245