SPH
LockingPtr.h
Go to the documentation of this file.
1 #pragma once
2 
7 
10 #include <atomic>
11 #include <mutex>
12 
14 
15 namespace Detail {
16 template <typename T, typename TMutex = std::mutex>
17 class LockingControlBlock : public ControlBlock<T> {
18 private:
19  TMutex mutex;
20  bool locked = false;
21 
22 public:
24  : ControlBlock<T>(ptr) {}
25 
26  void lock() {
27  mutex.lock();
28  locked = true;
29  }
30 
31  void unlock() {
32  mutex.unlock();
33  locked = false;
34  }
35 
36  bool isLocked() const {
37  return locked;
38  }
39 };
40 } // namespace Detail
41 
42 template <typename T>
43 class LockingPtr {
44  template <typename>
45  friend class LockingPtr;
46 
47 private:
48  SharedPtr<T> resource;
49  Detail::LockingControlBlock<T>* block = nullptr;
50 
51 public:
52  LockingPtr() = default;
53 
54  LockingPtr(T* ptr)
55  : resource(ptr, ptr ? alignedNew<Detail::LockingControlBlock<T>>(ptr) : nullptr) {
56  block = static_cast<Detail::LockingControlBlock<T>*>(resource.block);
57  }
58 
60  : LockingPtr(&*other) {
61  other.release();
62  }
63 
64  LockingPtr(const LockingPtr& other)
65  : resource(other.resource)
66  , block(other.block) {}
67 
68  template <typename T2>
69  LockingPtr(const LockingPtr<T2>& other)
70  : resource(other.resource) {
71  block = static_cast<Detail::LockingControlBlock<T>*>(resource.block);
72  }
73 
75  : resource(std::move(other.resource))
76  , block(other.block) {
77  other.block = nullptr;
78  }
79 
80  template <typename T2>
82  : resource(std::move(other.resource))
83  , block((Detail::LockingControlBlock<T>*)other.block) {
84  // type safety is already guarandeed by SharedPtr, so hopefully we can simply cast the block (the
85  // Locking block only adds a mutex that's independend on T)
86  other.block = nullptr;
87  }
88 
90  if (block) {
91  // make sure all the proxies are destroyed before killing the object
92  std::unique_lock<Detail::LockingControlBlock<T>> lock(*block);
93  }
94  }
95 
96  LockingPtr& operator=(const LockingPtr& other) {
97  if (block) {
98  std::unique_lock<Detail::LockingControlBlock<T>> lock(*block);
99  resource = other.resource;
100  block = other.block;
101  } else {
102  resource = other.resource;
103  block = other.block;
104  }
105  return *this;
106  }
107 
109  if (block) {
110  std::unique_lock<Detail::LockingControlBlock<T>> lock(*block);
111  resource = std::move(other.resource);
112  std::swap(block, other.block);
113  } else {
114  resource = std::move(other.resource);
115  std::swap(block, other.block);
116  }
117  return *this;
118  }
119 
120  class Proxy {
121  template <typename>
122  friend class LockingPtr;
123 
124  private:
125  RawPtr<T> ptr;
126  std::unique_lock<Detail::LockingControlBlock<T>> lock;
127 
128  Proxy()
129  : ptr(nullptr) {}
130 
132  : ptr(ptr.get())
133  , lock(*block) {
134  SPH_ASSERT(ptr != nullptr);
135  }
136 
137  public:
138  Proxy(Proxy&& proxy)
139  : ptr(proxy.ptr)
140  , lock(std::move(proxy.lock)) {}
141 
143  SPH_ASSERT(ptr != nullptr);
144  return ptr;
145  }
146 
147  T& operator*() {
148  SPH_ASSERT(ptr != nullptr);
149  return *ptr;
150  }
151 
153  return ptr;
154  }
155 
156  bool isLocked() {
157  return lock.owns_lock();
158  }
159 
160  void release() {
161  if (lock.owns_lock()) {
162  lock.unlock();
163  }
164  ptr = nullptr;
165  }
166  };
167 
168  struct ProxyRef {
170 
171  operator T&() {
172  return *proxy;
173  }
174  };
175 
176  Proxy lock() const {
177  if (resource) {
178  return Proxy(resource, block);
179  } else {
180  return Proxy();
181  }
182  }
183 
184  Proxy operator->() const {
185  SPH_ASSERT(resource);
186  return Proxy(resource, block);
187  }
188 
189  ProxyRef operator*() const {
190  SPH_ASSERT(resource);
191  return ProxyRef{ Proxy(resource, block) };
192  }
193 
194  explicit operator bool() const {
195  return bool(resource);
196  }
197 
198  bool operator!() const {
199  return !resource;
200  }
201 
202  void reset() {
203  if (block) {
204  std::unique_lock<Detail::LockingControlBlock<T>> lock(*block);
205  block = nullptr;
206  resource.reset();
207  } else {
208  resource.reset();
209  }
210  }
211 };
212 
213 template <typename T, typename... TArgs>
214 LockingPtr<T> makeLocking(TArgs&&... args) {
215  return LockingPtr<T>(alignedNew<T>(std::forward<TArgs>(args)...));
216 }
217 
Base class for utility wrappers (Optional, Variant, ...)
NAMESPACE_SPH_BEGIN INLINE T * alignedNew(TArgs &&... args)
Creates a new object of type T on heap, using aligned allocation.
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
LockingPtr< T > makeLocking(TArgs &&... args)
Definition: LockingPtr.h:214
#define NAMESPACE_SPH_END
Definition: Object.h:12
Wrapper of pointer that deletes the resource from destructor.
Definition: AutoPtr.h:15
Proxy(Proxy &&proxy)
Definition: LockingPtr.h:138
RawPtr< T > operator->()
Definition: LockingPtr.h:142
RawPtr< T > get()
Definition: LockingPtr.h:152
ProxyRef operator*() const
Definition: LockingPtr.h:189
LockingPtr(LockingPtr &&other)
Definition: LockingPtr.h:74
LockingPtr & operator=(const LockingPtr &other)
Definition: LockingPtr.h:96
LockingPtr()=default
LockingPtr(const LockingPtr< T2 > &other)
Definition: LockingPtr.h:69
void reset()
Definition: LockingPtr.h:202
Proxy lock() const
Definition: LockingPtr.h:176
Proxy operator->() const
Definition: LockingPtr.h:184
LockingPtr(AutoPtr< T > &&other)
Definition: LockingPtr.h:59
LockingPtr(T *ptr)
Definition: LockingPtr.h:54
LockingPtr(const LockingPtr &other)
Definition: LockingPtr.h:64
LockingPtr(LockingPtr< T2 > &&other)
Definition: LockingPtr.h:81
LockingPtr & operator=(LockingPtr &&other)
Definition: LockingPtr.h:108
bool operator!() const
Definition: LockingPtr.h:198
Non-owning wrapper of pointer.
Definition: RawPtr.h:19
Detail::ControlBlockHolder * block
Definition: SharedPtr.h:110
INLINE void reset()
Definition: SharedPtr.h:227
Overload of std::swap for Sph::Array.
Definition: Array.h:578
void swap(Sph::Array< T, TCounter > &ar1, Sph::Array< T, TCounter > &ar2)
Definition: Array.h:580