ADTF
Loading...
Searching...
No Matches
object_ptr_privates.h
Go to the documentation of this file.
1
7#pragma once
8
9#include "object_ptr_intf.h"
10
12
13#include <atomic>
14#include <cstddef>
15#include <exception>
16#include <memory>
17#include <new>
18#include <type_traits>
19#include <optional>
20
21namespace adtf
22{
23namespace ucom
24{
25namespace ant
26{
27
29namespace detail
30{
31
32// base class for the shared reference counter, reatained for legacy reasons only.
33class [[deprecated("Reliance on IObject::Destroy results in mismatched deallocation.")]]
34object_reference_counter final : public iobject_ptr_ref<size_t>
35{
36public: // types
38 typedef iobject_ptr_ref<size_t>::size_type size_type;
39
40public:
41 template<typename U>
42 explicit object_reference_counter(U* pObjImpl):
43 m_szUseCount(), m_szWeakCount(1), m_pObjImpl(ucom_cast<const IObject*>(pObjImpl))
44 {
45 }
46
47 virtual ~object_reference_counter()
48 {
49 }
50
51 virtual void IncreaseUseCount()
52 {
53 m_szUseCount++;
54 }
55
56 virtual void DecreaseUseCount()
57 {
58 // if the use count is zero, delete the shared resource
59 if (0 == --m_szUseCount)
60 {
61 Destroy();
62 DecreaseWeakCount();
63 }
64 }
65
66 virtual void IncreaseWeakCount()
67 {
68 m_szWeakCount++;
69 }
70
71 virtual void DecreaseWeakCount()
72 {
73 // if the weak count is zero, delete the resource manager
74 if (0 == --m_szWeakCount)
75 {
76 delete this;
77 }
78 }
79
80 virtual bool IncreaseUseCountNonZero()
81 {
82 size_type szExpected = m_szUseCount;
83 // Loop as long as either the use count got increased by one or the use count is 0
84 // This step is necessary to ensure the use count won't be zero between two non atomic
85 // operations
86 // Mind that szExpected is set to the current value by the compare and exchange method call.
87 while (0 != szExpected && !m_szUseCount.compare_exchange_weak(szExpected, szExpected + 1))
88 ;
89
90 return 0 != szExpected;
91 }
92
93private:
94 void Destroy()
95 {
96 if (m_pObjImpl)
97 {
98 // IObject::Destroy() is deprecated, but so is this whole class. Suppress the inner warning.
99#if defined(_MSC_VER)
100 #pragma warning(push)
101 #pragma warning(disable : 4996)
102#elif defined(__GNUC__)
103 #pragma GCC diagnostic push
104 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
105#endif
106 m_pObjImpl->Destroy();
107#if defined(_MSC_VER)
108 #pragma warning(pop)
109#elif defined(__GNUC__)
110 #pragma GCC diagnostic pop
111#endif
112 m_pObjImpl = nullptr;
113 }
114 }
115
116private: // types
117 ::std::atomic<size_type> m_szUseCount;
118 ::std::atomic<size_type> m_szWeakCount;
119 IObject const* m_pObjImpl;
120};
121
122} // namespace detail
123} // namespace ant
124namespace vision
125{
126namespace detail
127{
128#ifdef _MSC_VER
129 #pragma warning(push)
130 // Disable warning about (necessary) padding bytes after m_aObjectStorage
131 #pragma warning(disable : 4324)
132#endif
133
134template<typename T, bool Fused>
135struct fused_storage;
136
137template<typename T>
138struct fused_storage<T, true>
139{
140 union aligned_storage
141 {
142 T oInstance;
143 constexpr aligned_storage() noexcept
144 {
145 // oInstance is constructed by fused_object_reference_counter
146 }
147 ~aligned_storage() noexcept
148 {
149 // oInstance is destroyed by fused_object_reference_counter
150 }
151 } oBuffer;
152};
153
154template<typename T>
155struct fused_storage<T, false>
156{
157};
158
159// base class for the allocator aware shared reference counter with optionally fused allocation
160template<typename T, typename Allocator, bool Fused = true>
161class fused_object_reference_counter final : public ant::detail::iobject_ptr_ref<size_t>
162{
163public:
164 using allocator_type =
165 typename ::std::allocator_traits<Allocator>::template rebind_alloc<fused_object_reference_counter>;
166 using object_allocator_type = ::std::
167 conditional_t<Fused, allocator_type, typename ::std::allocator_traits<Allocator>::template rebind_alloc<T>>;
168
169 template<typename ExternalAllocator, typename... Args>
170 static fused_object_reference_counter* Create(ExternalAllocator&& oExternalAllocator, Args&&... args)
171 {
172 allocator_type oAllocator(::std::forward<ExternalAllocator>(oExternalAllocator));
173 static_assert(::std::is_same_v<typename ::std::allocator_traits<allocator_type>::value_type,
174 fused_object_reference_counter>);
175 fused_object_reference_counter* pCounter = ::std::allocator_traits<allocator_type>::allocate(oAllocator, 1);
176 try
177 {
178 ::std::allocator_traits<allocator_type>::construct(oAllocator, pCounter, oAllocator,
179 ::std::forward<Args>(args)...);
180 }
181 catch (...)
182 {
183 ::std::allocator_traits<allocator_type>::deallocate(oAllocator, pCounter, 1);
184 throw;
185 }
186 return std::launder(pCounter);
187 }
188
189 fused_object_reference_counter() = delete;
190
191 template<typename... Args>
192 fused_object_reference_counter(const allocator_type& allocator, Args&&... args):
193 m_oAllocator(allocator), m_szUseCount(0), m_szWeakCount(1), m_pObject(nullptr)
194 {
195 object_allocator_type oObjectAllocator(m_oAllocator);
196 T* pObject;
197 if constexpr (Fused)
198 {
199 pObject = &m_sObjectStorage.oBuffer.oInstance;
200 }
201 else
202 {
203 pObject = ::std::allocator_traits<object_allocator_type>::allocate(oObjectAllocator, 1);
204 }
205 try
206 {
207 ::std::allocator_traits<object_allocator_type>::construct(oObjectAllocator, pObject,
208 ::std::forward<Args>(args)...);
209 }
210 catch (...)
211 {
212 if constexpr (!Fused)
213 {
214 ::std::allocator_traits<object_allocator_type>::deallocate(oObjectAllocator, pObject, 1);
215 }
216 throw;
217 }
218 m_pObject = std::launder(pObject);
219 }
220
221 ~fused_object_reference_counter() = default;
222
223 T* GetSharedObject()
224 {
225 return m_pObject;
226 }
227
228private:
229 void IncreaseUseCount() noexcept final override
230 {
231 m_szUseCount.fetch_add(1, ::std::memory_order_relaxed);
232 }
233
234 void DecreaseUseCount() final override
235 {
236 if (1 == m_szUseCount.fetch_sub(1, ::std::memory_order_release))
237 {
238 ::std::atomic_thread_fence(::std::memory_order_acquire);
239 ::std::optional<::std::exception_ptr> eptr;
240 object_allocator_type oObjectAllocator(m_oAllocator);
241 try
242 {
243 ::std::allocator_traits<object_allocator_type>::destroy(oObjectAllocator, m_pObject);
244 }
245 catch (...)
246 {
247 eptr = ::std::current_exception();
248 }
249 if constexpr (!Fused)
250 {
251 ::std::allocator_traits<object_allocator_type>::deallocate(oObjectAllocator, m_pObject, 1);
252 }
253 m_pObject = nullptr;
254 DecreaseWeakCount();
255 if (eptr)
256 {
257 ::std::rethrow_exception(std::move(*eptr));
258 }
259 }
260 }
261
262 void IncreaseWeakCount() noexcept final override
263 {
264 m_szWeakCount.fetch_add(1, ::std::memory_order_relaxed);
265 }
266
267 void DecreaseWeakCount() final override
268 {
269 if (1 == m_szWeakCount.fetch_sub(1, ::std::memory_order_release))
270 {
271 ::std::atomic_thread_fence(::std::memory_order_acquire);
272 ::std::optional<::std::exception_ptr> eptr;
273 static_assert(::std::is_same_v<typename ::std::allocator_traits<allocator_type>::value_type,
274 fused_object_reference_counter>);
275 allocator_type oAllocator(std::move(m_oAllocator));
276 auto pSelf = std::launder(this);
277 try
278 {
279 ::std::allocator_traits<allocator_type>::destroy(oAllocator, pSelf);
280 }
281 catch (...)
282 {
283 eptr = std::current_exception();
284 }
285 ::std::allocator_traits<allocator_type>::deallocate(oAllocator, pSelf, 1);
286 if (eptr)
287 {
288 ::std::rethrow_exception(std::move(*eptr));
289 }
290 }
291 }
292
293 bool IncreaseUseCountNonZero() noexcept final override
294 {
295 size_type szExpected = m_szUseCount.load(std::memory_order_relaxed);
296 while (0 != szExpected &&
297 !m_szUseCount.compare_exchange_weak(szExpected, szExpected + 1, std::memory_order_acquire))
298 ;
299
300 return 0 != szExpected;
301 }
302
303 allocator_type m_oAllocator;
304 ::std::atomic<size_t> m_szUseCount;
305 ::std::atomic<size_t> m_szWeakCount;
306 T* m_pObject;
307
308 static constexpr std::size_t hardware_destructive_interference_size = 64;
309 // Keep the bookkeeping stuff in a cache line separated from the main object storage.
310 alignas((::std::max) (hardware_destructive_interference_size,
311 alignof(fused_storage<T, Fused>))) fused_storage<T, Fused> m_sObjectStorage;
312};
313
314#ifdef _MSC_VER
315 #pragma warning(pop)
316#endif
317} // namespace detail
318} // namespace vision
319} // namespace ucom
320} // namespace adtf
Namespace for all internally used functionality implemented.
Definition class_info.h:17
Namespace for all functionality of the ADTF UCOM SDK provided since v3.0.
Definition test_runtime.h:14
InterfacePointerType ucom_cast(ObjectPointerType i_pObject) noexcept
Definition ucom_cast.h:157
Namespace for all functionality of the ADTF UCOM SDK provided since v3.21.
Definition plugin_logging_intf.h:48
Namespace for the ADTF uCOM SDK.
Definition adtf_system.h:324
ant::IObject IObject
Alias always bringing the latest version of ant::IObject into scope.
Definition object_intf.h:109
Namespace for all functionality provided by ADTF and its SDKs.
Definition adtf_client_connector.h:14