Core: new blenlib library for implicit-sharing #105994
|
@ -54,7 +54,7 @@ class AnonymousAttributeID : public ImplicitSharingMixin {
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
|
/** Wrapper for #AnonymousAttributeID that avoids manual reference counting. */
|
||||||
using AutoAnonymousAttributeID = ImplicitSharePtr<const AnonymousAttributeID>;
|
using AutoAnonymousAttributeID = ImplicitSharingPtr<const AnonymousAttributeID>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A set of anonymous attribute names that is passed around in geometry nodes.
|
* A set of anonymous attribute names that is passed around in geometry nodes.
|
||||||
|
|
|
@ -101,7 +101,7 @@ inline constexpr bool is_geometry_component_v = std::is_base_of_v<GeometryCompon
|
||||||
*/
|
*/
|
||||||
struct GeometrySet {
|
struct GeometrySet {
|
||||||
private:
|
private:
|
||||||
using GeometryComponentPtr = blender::ImplicitSharePtr<class GeometryComponent>;
|
using GeometryComponentPtr = blender::ImplicitSharingPtr<class GeometryComponent>;
|
||||||
/* Indexed by #GeometryComponentType. */
|
/* Indexed by #GeometryComponentType. */
|
||||||
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
|
std::array<GeometryComponentPtr, GEO_COMPONENT_TYPE_ENUM_SIZE> components_;
|
||||||
|
|
||||||
|
|
|
@ -15,26 +15,27 @@
|
||||||
namespace blender {
|
namespace blender {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* #bCopyOnWrite allows implementing copy-on-write behavior, i.e. it allows sharing read-only data
|
* #ImplicitSharingInfo is the core data structure for implicit sharing in Blender. Implicit
|
||||||
* between multiple independend systems (e.g. meshes). The data is only copied when it is shared
|
* sharing is a technique that avoids copying data when it is not necessary. This results in better
|
||||||
* and is about to be modified. This is in contrast to making copies before it is actually known
|
* memory usage and performance. Only read-only data can be shared, because otherwise multiple
|
||||||
* that it is necessary.
|
* owners might want to change the data in conflicting ways.
|
||||||
*
|
*
|
||||||
* Internally, this is mostly just a glorified reference count. If the reference count is 1, the
|
* To determine whether data is shared, #ImplicitSharingInfo keeps a user count. If the count is 1,
|
||||||
* data only has a single owner and is mutable. If it is larger than 1, it is shared and must be
|
* the data only has a single owner and is therefore mutable. If some code wants to modify data
|
||||||
* logically const.
|
* that is currently shared, it has to make a copy first.
|
||||||
|
* This behavior is also called "copy on write".
|
||||||
*
|
*
|
||||||
* On top of containing a reference count, #bCopyOnWrite also knows how to destruct the referenced
|
* On top of containing a reference count, #ImplicitSharingInfo also knows how to destruct the
|
||||||
* data. This is important because the code freeing the data in the end might not know how it was
|
* referenced data. This is important because the code freeing the data in the end might not know
|
||||||
* allocated (for example, it doesn't know whether an array was allocated using the system or
|
* how it was allocated (for example, it doesn't know whether an array was allocated using the
|
||||||
* guarded allocator).
|
* system or guarded allocator).
|
||||||
*
|
*
|
||||||
* #bCopyOnWrite is used in two ways:
|
* #ImplicitSharingInfo can be used in two ways:
|
||||||
* - It can be allocated separately from the referenced data as is typically the case with raw
|
* - It can be allocated separately from the referenced data. This is used when the shared data is
|
||||||
* arrays (e.g. for mesh attributes).
|
* e.g. a plain data array.
|
||||||
* - It can be embedded into another struct. For that it's best to use #ImplicitSharingMixin.
|
* - It can be embedded into another struct. For that it's best to use #ImplicitSharingMixin.
|
||||||
*/
|
*/
|
||||||
struct ImplicitSharingInfo : blender::NonCopyable, blender::NonMovable {
|
class ImplicitSharingInfo : blender::NonCopyable, blender::NonMovable {
|
||||||
private:
|
private:
|
||||||
mutable std::atomic<int> users_;
|
mutable std::atomic<int> users_;
|
||||||
|
|
||||||
|
@ -74,14 +75,14 @@ struct ImplicitSharingInfo : blender::NonCopyable, blender::NonMovable {
|
||||||
}
|
}
|
||||||
|
|
||||||
brecht marked this conversation as resolved
Outdated
|
|||||||
private:
|
private:
|
||||||
/** Has to free the #bCopyOnWrite and the referenced data. */
|
/** Has to free the #ImplicitSharingInfo and the referenced data. */
|
||||||
virtual void delete_self_with_data() = 0;
|
virtual void delete_self_with_data() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes it easy to embed copy-on-write behavior into a struct.
|
* Makes it easy to embed implicit-sharing behavior into a struct.
|
||||||
JacquesLucke marked this conversation as resolved
Outdated
Hans Goudey
commented
This comment is a bit hard to follow, maybe replacing This comment is a bit hard to follow, maybe replacing `the subclass` with something more specific would help (like saying the ptr class can be used with a class that derives from the mixin class).
|
|||||||
*/
|
*/
|
||||||
struct ImplicitSharingMixin : public ImplicitSharingInfo {
|
class ImplicitSharingMixin : public ImplicitSharingInfo {
|
||||||
public:
|
public:
|
||||||
ImplicitSharingMixin() : ImplicitSharingInfo(1)
|
ImplicitSharingMixin() : ImplicitSharingInfo(1)
|
||||||
{
|
{
|
||||||
|
|
|
@ -10,33 +10,38 @@
|
||||||
|
|
||||||
namespace blender {
|
namespace blender {
|
||||||
|
|
||||||
template<typename T> class ImplicitSharePtr {
|
/**
|
||||||
|
* #ImplicitSharingPtr is a smart pointer that manages implicit sharing. It's designed to work with
|
||||||
|
* types that derive from #ImplicitSharingMixin. It is fairly similar to #std::shared_ptr but
|
||||||
|
* expects the reference count to be embedded in the data.
|
||||||
JacquesLucke marked this conversation as resolved
Outdated
Hans Goudey
commented
`expects` -> `requires`, right? `std::make_shared` will allocate the use count with the object so it's helpful to clarify this a bit.
|
|||||||
|
*/
|
||||||
|
template<typename T> class ImplicitSharingPtr {
|
||||||
private:
|
private:
|
||||||
T *data_ = nullptr;
|
T *data_ = nullptr;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ImplicitSharePtr() = default;
|
ImplicitSharingPtr() = default;
|
||||||
|
|
||||||
ImplicitSharePtr(T *data) : data_(data)
|
ImplicitSharingPtr(T *data) : data_(data)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
ImplicitSharePtr(const ImplicitSharePtr &other) : data_(other.data_)
|
ImplicitSharingPtr(const ImplicitSharingPtr &other) : data_(other.data_)
|
||||||
{
|
{
|
||||||
this->add_user(data_);
|
this->add_user(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImplicitSharePtr(ImplicitSharePtr &&other) : data_(other.data_)
|
ImplicitSharingPtr(ImplicitSharingPtr &&other) : data_(other.data_)
|
||||||
{
|
{
|
||||||
other.data_ = nullptr;
|
other.data_ = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
~ImplicitSharePtr()
|
~ImplicitSharingPtr()
|
||||||
{
|
{
|
||||||
this->remove_user_and_delete_if_last(data_);
|
this->remove_user_and_delete_if_last(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImplicitSharePtr &operator=(const ImplicitSharePtr &other)
|
ImplicitSharingPtr &operator=(const ImplicitSharingPtr &other)
|
||||||
{
|
{
|
||||||
if (this == &other) {
|
if (this == &other) {
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -48,7 +53,7 @@ template<typename T> class ImplicitSharePtr {
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImplicitSharePtr &operator=(ImplicitSharePtr &&other)
|
ImplicitSharingPtr &operator=(ImplicitSharingPtr &&other)
|
||||||
{
|
{
|
||||||
if (this == &other) {
|
if (this == &other) {
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -122,7 +127,7 @@ template<typename T> class ImplicitSharePtr {
|
||||||
return get_default_hash(data_);
|
return get_default_hash(data_);
|
||||||
}
|
}
|
||||||
|
|
||||||
friend bool operator==(const ImplicitSharePtr &a, const ImplicitSharePtr &b)
|
friend bool operator==(const ImplicitSharingPtr &a, const ImplicitSharingPtr &b)
|
||||||
{
|
{
|
||||||
return a.data_ == b.data_;
|
return a.data_ == b.data_;
|
||||||
}
|
}
|
||||||
|
|
|
@ -226,8 +226,8 @@ struct GatherTasks {
|
||||||
|
|
||||||
/* Volumes only have very simple support currently. Only the first found volume is put into the
|
/* Volumes only have very simple support currently. Only the first found volume is put into the
|
||||||
* output. */
|
* output. */
|
||||||
ImplicitSharePtr<const VolumeComponent> first_volume;
|
ImplicitSharingPtr<const VolumeComponent> first_volume;
|
||||||
ImplicitSharePtr<const GeometryComponentEditData> first_edit_data;
|
ImplicitSharingPtr<const GeometryComponentEditData> first_edit_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Current offsets while during the gather operation. */
|
/** Current offsets while during the gather operation. */
|
||||||
|
|
Loading…
Reference in New Issue
My understanding is that user count decrement requires
std::memory_order_acq_rel
, while increment can usestd::memory_order_relaxed
.For example libc++ does this for shared_ptr. More details:
https://stackoverflow.com/questions/48124031/stdmemory-order-relaxed-atomicity-with-respect-to-the-same-atomic-variable