Files
test/source/blender/blenlib/tests/BLI_implicit_sharing_test.cc
Jacques Lucke 7730ca2b9d BLI: improve const handling in ImplicitSharingPtr
The constness of the `ImplicitSharingPtr` does not imply the constness of the
referenced data, because that is determined by the user count. Therefore,
`ImplicitSharingPtr` should never give a non-const pointer to the underlying data.
Instead, one always has to check the user count, before one can do a `const_cast`.

Pull Request: https://projects.blender.org/blender/blender/pulls/115652
2023-12-01 11:19:39 +01:00

118 lines
3.1 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "MEM_guardedalloc.h"
#include "BLI_implicit_sharing_ptr.hh"
#include "testing/testing.h"
namespace blender::tests {
class ImplicitlySharedData : public ImplicitSharingMixin {
public:
ImplicitSharingPtr<ImplicitlySharedData> copy() const
{
return ImplicitSharingPtr<ImplicitlySharedData>(MEM_new<ImplicitlySharedData>(__func__));
}
void delete_self() override
{
MEM_delete(this);
}
};
class SharedDataContainer {
private:
ImplicitSharingPtr<ImplicitlySharedData> data_;
public:
SharedDataContainer() : data_(MEM_new<ImplicitlySharedData>(__func__)) {}
const ImplicitSharingInfo *sharing_info() const
{
return data_.get();
}
const ImplicitlySharedData *get_for_read() const
{
return data_.get();
}
ImplicitlySharedData *get_for_write()
{
if (!data_) {
return nullptr;
}
if (data_->is_mutable()) {
data_->tag_ensured_mutable();
}
else {
data_ = data_->copy();
}
return const_cast<ImplicitlySharedData *>(data_.get());
}
};
TEST(implicit_sharing, CopyOnWriteAccess)
{
/* Create the initial data. */
SharedDataContainer a;
EXPECT_NE(a.get_for_read(), nullptr);
/* a and b share the same underlying data now. */
SharedDataContainer b = a;
EXPECT_EQ(a.get_for_read(), b.get_for_read());
/* c now shares the data with a and b. */
SharedDataContainer c = a;
EXPECT_EQ(b.get_for_read(), c.get_for_read());
/* Retrieving write access on b should make a copy because the data is shared. */
ImplicitlySharedData *data_b1 = b.get_for_write();
EXPECT_NE(data_b1, nullptr);
EXPECT_EQ(data_b1, b.get_for_read());
EXPECT_NE(data_b1, a.get_for_read());
EXPECT_NE(data_b1, c.get_for_read());
/* Retrieving the same write access again should *not* make another copy. */
ImplicitlySharedData *data_b2 = b.get_for_write();
EXPECT_EQ(data_b1, data_b2);
/* Moving b should also move the data. b then does not have ownership anymore. Since the data in
* b only had one owner, the data is still mutable now that d is the owner. */
SharedDataContainer d = std::move(b);
EXPECT_EQ(b.get_for_read(), nullptr);
EXPECT_EQ(b.get_for_write(), nullptr);
EXPECT_EQ(d.get_for_read(), data_b1);
EXPECT_EQ(d.get_for_write(), data_b1);
}
TEST(implicit_sharing, WeakUser)
{
SharedDataContainer a;
const ImplicitSharingInfo *sharing_info = a.sharing_info();
EXPECT_FALSE(sharing_info->is_expired());
EXPECT_TRUE(sharing_info->is_mutable());
sharing_info->add_weak_user();
EXPECT_FALSE(sharing_info->is_expired());
EXPECT_TRUE(sharing_info->is_mutable());
a = {};
EXPECT_TRUE(sharing_info->is_expired());
sharing_info->remove_weak_user_and_delete_if_last();
}
TEST(implicit_sharing, Version)
{
SharedDataContainer a;
const ImplicitSharingInfo *sharing_info = a.sharing_info();
const int old_version = sharing_info->version();
a.get_for_read();
EXPECT_EQ(old_version, sharing_info->version());
a.get_for_write();
EXPECT_LT(old_version, sharing_info->version());
}
} // namespace blender::tests