Blender V4.3
BLI_implicit_sharing.hh
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#pragma once
6
11#include <atomic>
12
13#include "BLI_assert.h"
14#include "BLI_utility_mixins.hh"
15
16#include "MEM_guardedalloc.h"
17
18namespace blender {
19
42 private:
50 mutable std::atomic<int> strong_users_ = 1;
57 mutable std::atomic<int> weak_users_ = 1;
63 mutable std::atomic<int64_t> version_ = 0;
64
65 public:
67 {
68 BLI_assert(strong_users_ == 0);
69 BLI_assert(weak_users_ == 0);
70 }
71
73 bool is_mutable() const
74 {
75 return strong_users_.load(std::memory_order_relaxed) == 1;
76 }
77
82 bool is_expired() const
83 {
84 return strong_users_.load(std::memory_order_acquire) == 0;
85 }
86
88 void add_user() const
89 {
90 BLI_assert(!this->is_expired());
91 strong_users_.fetch_add(1, std::memory_order_relaxed);
92 }
93
102 void add_weak_user() const
103 {
104 weak_users_.fetch_add(1, std::memory_order_relaxed);
105 }
106
113 {
114 BLI_assert(this->is_mutable());
115 /* This might not need an atomic increment when the #version method below is only called when
116 * the code calling it is a strong user of this sharing info. Better be safe and use an atomic
117 * for now. */
118 version_.fetch_add(1, std::memory_order_acq_rel);
119 }
120
126 {
127 return version_.load(std::memory_order_acquire);
128 }
129
130 int strong_users() const
131 {
132 return strong_users_.load(std::memory_order_acquire);
133 }
134
140 {
141 const int old_user_count = strong_users_.fetch_sub(1, std::memory_order_acq_rel);
142 BLI_assert(old_user_count >= 1);
143 const bool was_last_user = old_user_count == 1;
144 if (was_last_user) {
145 const int old_weak_user_count = weak_users_.load(std::memory_order_acquire);
146 BLI_assert(old_weak_user_count >= 1);
147 if (old_weak_user_count == 1) {
148 /* If the weak user count is 1 it means that there is no actual weak user. The 1 just
149 * indicates that there was still at least one strong user. */
150 weak_users_ = 0;
151 const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
152 }
153 else {
154 /* There is still at least one actual weak user, so don't free the sharing info yet. The
155 * data can be freed though. */
156 const_cast<ImplicitSharingInfo *>(this)->delete_data_only();
157 /* Also remove the "fake" weak user that indicated that there was at least one strong
158 * user. */
160 }
161 }
162 }
163
169 {
170 const int old_weak_user_count = weak_users_.fetch_sub(1, std::memory_order_acq_rel);
171 BLI_assert(old_weak_user_count >= 1);
172 const bool was_last_weak_user = old_weak_user_count == 1;
173 if (was_last_weak_user) {
174 /* It's possible that the data has been freed before already, but now it is definitely freed
175 * together with the sharing info. */
176 const_cast<ImplicitSharingInfo *>(this)->delete_self_with_data();
177 }
178 }
179
180 private:
183 virtual void delete_self_with_data() = 0;
185 virtual void delete_data_only() {}
186};
187
193
194 private:
195 void delete_self_with_data() override
196 {
197 /* Can't use `delete this` here, because we don't know what allocator was used. */
198 this->delete_self();
199 }
200
201 virtual void delete_self() = 0;
202};
203
208template<typename T> class ImplicitSharedValue : public ImplicitSharingInfo {
209 public:
211
212 template<typename... Args>
213 ImplicitSharedValue(Args &&...args) : data(std::forward<Args>(args)...)
214 {
215 }
216
217#ifdef WITH_CXX_GUARDEDALLOC
218 MEM_CXX_CLASS_ALLOC_FUNCS("ImplicitSharedValue");
219#endif
220
221 private:
222 void delete_self_with_data() override
223 {
224 delete this;
225 }
226};
227
233 const void *data = nullptr;
234};
235
236namespace implicit_sharing {
237
238namespace detail {
239
240void *resize_trivial_array_impl(void *old_data,
241 int64_t old_size,
242 int64_t new_size,
243 int64_t alignment,
244 const ImplicitSharingInfo **sharing_info);
245void *make_trivial_data_mutable_impl(void *old_data,
246 int64_t size,
247 int64_t alignment,
248 const ImplicitSharingInfo **sharing_info);
249
250} // namespace detail
251
256template<typename T>
257void copy_shared_pointer(T *src_ptr,
258 const ImplicitSharingInfo *src_sharing_info,
259 T **r_dst_ptr,
260 const ImplicitSharingInfo **r_dst_sharing_info)
261{
262 *r_dst_ptr = src_ptr;
263 *r_dst_sharing_info = src_sharing_info;
264 if (*r_dst_ptr) {
265 BLI_assert(*r_dst_sharing_info != nullptr);
266 (*r_dst_sharing_info)->add_user();
267 }
268}
269
273template<typename T> void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
274{
275 if (*sharing_info) {
276 BLI_assert(*data != nullptr);
277 (*sharing_info)->remove_user_and_delete_if_last();
278 }
279 *data = nullptr;
280 *sharing_info = nullptr;
281}
282
287const ImplicitSharingInfo *info_for_mem_free(void *data);
288
292template<typename T>
294 const ImplicitSharingInfo **sharing_info,
295 const int64_t size)
296{
297 *data = static_cast<T *>(
298 detail::make_trivial_data_mutable_impl(*data, sizeof(T) * size, alignof(T), sharing_info));
299}
300
305template<typename T>
307 const ImplicitSharingInfo **sharing_info,
308 int64_t old_size,
309 int64_t new_size)
310{
311 *data = static_cast<T *>(detail::resize_trivial_array_impl(
312 *data, sizeof(T) * old_size, sizeof(T) * new_size, alignof(T), sharing_info));
313}
314
315} // namespace implicit_sharing
316
317} // namespace blender
#define BLI_assert(a)
Definition BLI_assert.h:50
Read Guarded memory(de)allocation.
void * resize_trivial_array_impl(void *old_data, int64_t old_size, int64_t new_size, int64_t alignment, const ImplicitSharingInfo **sharing_info)
void * make_trivial_data_mutable_impl(void *old_data, int64_t size, int64_t alignment, const ImplicitSharingInfo **sharing_info)
void resize_trivial_array(T **data, const ImplicitSharingInfo **sharing_info, int64_t old_size, int64_t new_size)
void copy_shared_pointer(T *src_ptr, const ImplicitSharingInfo *src_sharing_info, T **r_dst_ptr, const ImplicitSharingInfo **r_dst_sharing_info)
const ImplicitSharingInfo * info_for_mem_free(void *data)
void free_shared_data(T **data, const ImplicitSharingInfo **sharing_info)
void make_trivial_data_mutable(T **data, const ImplicitSharingInfo **sharing_info, const int64_t size)
__int64 int64_t
Definition stdint.h:89
const ImplicitSharingInfo * sharing_info