Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Core][PVS] Add PointerVectorSet::MutablePass to allow push_back safely. #12839

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
217 changes: 153 additions & 64 deletions kratos/containers/pointer_vector_set.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <sstream>
#include <cstddef>
#include <utility>
#include <type_traits>
#include <type_traits>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicated


// External includes
#include <boost/iterator/indirect_iterator.hpp>
Expand Down Expand Up @@ -107,6 +109,99 @@ class PointerVectorSet final
using ptr_const_reverse_iterator = typename TContainerType::const_reverse_iterator;
using difference_type = typename TContainerType::difference_type;

///@}
///@name Class definitions
///@{

/// @brief Unrestricted accessor for the pointer vector set
/// @details This class provides unrestricted access the underlying std::vector of the pointer vector
/// set which may make the pointer vector set unsorted. Hence, once an object of this is created, the
/// methods which utilize the sorted property of pointer vector set are frozen. At the destruction of this class,
/// pointer vector set is sorted and made unique, and the frozen methods are released.
/// @param
/// @return
class MutablePass
{
///@name Life cycle
///@{

// Constructor
MutablePass(PointerVectorSet& rContainer)
: mrContainer(rContainer)
{
KRATOS_CRITICAL_SECTION
KRATOS_ERROR_IF(mrContainer.mHasMutablePass)
<< "A mutable pass is active. Hence, creation of an another MutablePass is prohibited.";
mrContainer.mHasMutablePass = true;
}

///@}

public:
///@name Life cycle
///@{

MutablePass(const MutablePass& rOther) = delete;

MutablePass(MutablePass&& rOther) noexcept = default;

// Destructor
~MutablePass()
{
// Sort the PointerVectorSet data
std::sort(mrContainer.mData.begin(), mrContainer.mData.end(), CompareKey());

// Make the entities unique
typename TContainerType::iterator new_end_it = std::unique(mrContainer.mData.begin(), mrContainer.mData.end(), EqualKeyTo());

// remove the duplicated entities.
mrContainer.mData.erase(new_end_it, mrContainer.mData.end());

// unfreeze the methods which dependent on the sorted state of the PointerVectorSet
mrContainer.mHasMutablePass = false;
}

///@name Public operators
///@{

MutablePass& operator=(const MutablePass& rOther) = delete;

///@}
///@name Public methods
///@{

void shrink_to_fit()
{
mrContainer.mData.shrink_to_fit();
}

void reserve(size_type NewCapacity)
{
mrContainer.mData.reserve(NewCapacity);
}

void push_back(TPointerType pValue)
{
mrContainer.mData.push_back(pValue);
}

///@}

private:
///@name Member variables
///@{

PointerVectorSet& mrContainer;

///@}
///@name Friend classes
///@{

friend class PointerVectorSet;

///@}
};

///@}
///@name Life Cycle
///@{
Expand Down Expand Up @@ -178,29 +273,15 @@ class PointerVectorSet final
*/
TDataType& operator[](const key_type& Key)
{
ptr_iterator sorted_part_end;

if (mData.size() - mSortedPartSize >= mMaxBufferSize) {
Sort();
sorted_part_end = mData.end();
KRATOS_ERROR_IF(mHasMutablePass)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No go...you are adding an overhead to every access...

<< "A mutable pass is active. Hence, use of PointerVectorSet::operator[] is prohibited.";
ptr_iterator i(std::lower_bound(mData.begin(), mData.end(), Key, CompareKey()));
if (i != mData.end() && EqualKeyTo(Key)(*i)) {
return **i;
} else {
sorted_part_end = mData.begin() + mSortedPartSize;
}

ptr_iterator i(std::lower_bound(mData.begin(), sorted_part_end, Key, CompareKey()));
if (i == sorted_part_end) {
mSortedPartSize++;
return **mData.insert(sorted_part_end, TPointerType(new TDataType(Key)));
static_assert(!std::is_pointer_v<TPointerType>, "Raw pointers are not supported.");
return **mData.insert(i, TPointerType(new TDataType(Key)));
}

if (!EqualKeyTo(Key)(*i)) {
if ((i = std::find_if(sorted_part_end, mData.end(), EqualKeyTo(Key))) == mData.end()) {
mData.push_back(TPointerType(new TDataType(Key)));
return **(mData.end() - 1);
}
}

return **i;
}

/**
Expand All @@ -213,27 +294,15 @@ class PointerVectorSet final
*/
pointer& operator()(const key_type& Key)
{
ptr_iterator sorted_part_end;

if (mData.size() - mSortedPartSize >= mMaxBufferSize) {
Sort();
sorted_part_end = mData.end();
} else
sorted_part_end = mData.begin() + mSortedPartSize;

ptr_iterator i(std::lower_bound(mData.begin(), sorted_part_end, Key, CompareKey()));
if (i == sorted_part_end) {
mSortedPartSize++;
return *mData.insert(sorted_part_end, TPointerType(new TDataType(Key)));
KRATOS_ERROR_IF(mHasMutablePass)
<< "A mutable pass is active. Hence, use of PointerVectorSet::operator() is prohibited.";
ptr_iterator i(std::lower_bound(mData.begin(), mData.end(), Key, CompareKey()));
if (i != mData.end() && EqualKeyTo(Key)(*i)) {
return *i;
} else {
static_assert(!std::is_pointer_v<TPointerType>, "Raw pointers are not supported.");
return *mData.insert(i, TPointerType(new TDataType(Key)));
}

if (!EqualKeyTo(Key)(*i))
if ((i = std::find_if(sorted_part_end, mData.end(), EqualKeyTo(Key))) == mData.end()) {
mData.push_back(TPointerType(new TDataType(Key)));
return *(mData.end() - 1);
}

return *i;
}

/**
Expand Down Expand Up @@ -568,6 +637,8 @@ class PointerVectorSet final
*/
iterator insert(const TPointerType& value)
{
KRATOS_ERROR_IF(mHasMutablePass)
<< "A mutable pass is active. Hence, use of PointerVectorSet::insert(value) is prohibited.";
auto itr_pos = std::lower_bound(mData.begin(), mData.end(), KeyOf(*value), CompareKey());
if (itr_pos == mData.end()) {
// the position to insert is at the end.
Expand Down Expand Up @@ -596,6 +667,8 @@ class PointerVectorSet final
*/
iterator insert(const_iterator position_hint, const TPointerType& value)
{
KRATOS_ERROR_IF(mHasMutablePass)
<< "A mutable pass is active. Hence, use of PointerVectorSet::insert(position_hint, value) is prohibited.";
if (empty()) {
// the dataset is empty. So use push back.
mData.push_back(value);
Expand Down Expand Up @@ -647,6 +720,8 @@ class PointerVectorSet final
template <class InputIterator>
void insert(InputIterator first, InputIterator last)
{
KRATOS_ERROR_IF(mHasMutablePass)
<< "A mutable pass is active. Hence, use of PointerVectorSet::insert(first, last) is prohibited.";
// first sorts the input iterators and make the input unique.
std::sort(first, last, CompareKey());
auto new_last = std::unique(first, last, EqualKeyTo());
Expand Down Expand Up @@ -741,20 +816,14 @@ class PointerVectorSet final
*/
iterator find(const key_type& Key)
{
ptr_iterator sorted_part_end;

if (mData.size() - mSortedPartSize >= mMaxBufferSize) {
Sort();
sorted_part_end = mData.end();
} else
sorted_part_end = mData.begin() + mSortedPartSize;

ptr_iterator i(std::lower_bound(mData.begin(), sorted_part_end, Key, CompareKey()));
if (i == sorted_part_end || (!EqualKeyTo(Key)(*i)))
if ((i = std::find_if(sorted_part_end, mData.end(), EqualKeyTo(Key))) == mData.end())
return mData.end();

return i;
KRATOS_ERROR_IF(mHasMutablePass)
<< "A mutable pass is active. Hence, use of PointerVectorSet::find(key) is prohibited.";
ptr_iterator i(std::lower_bound(mData.begin(), mData.end(), Key, CompareKey()));
if (i != mData.end() && EqualKeyTo(Key)(*i)) {
return i;
} else {
return mData.end();
}
}

/**
Expand All @@ -767,14 +836,14 @@ class PointerVectorSet final
*/
const_iterator find(const key_type& Key) const
{
ptr_const_iterator sorted_part_end(mData.begin() + mSortedPartSize);

ptr_const_iterator i(std::lower_bound(mData.begin(), sorted_part_end, Key, CompareKey()));
if (i == sorted_part_end || (!EqualKeyTo(Key)(*i)))
if ((i = std::find_if(sorted_part_end, mData.end(), EqualKeyTo(Key))) == mData.end())
return mData.end();

return const_iterator(i);
KRATOS_ERROR_IF(mHasMutablePass)
<< "A mutable pass is active. Hence, use of PointerVectorSet::find(key) is prohibited.";
ptr_const_iterator i(std::lower_bound(mData.begin(), mData.end(), Key, CompareKey()));
if (i != mData.end() && EqualKeyTo(Key)(*i)) {
return const_iterator(i);
} else {
return mData.end();
}
}

/**
Expand Down Expand Up @@ -838,6 +907,11 @@ class PointerVectorSet final
///@name Access
///@{

MutablePass GetMutablePass()
{
return MutablePass(*this);
}

/** Gives a reference to underly normal container. */
TContainerType& GetContainer()
{
Expand Down Expand Up @@ -1062,6 +1136,8 @@ class PointerVectorSet final
/// The maximum buffer size for data storage.
size_type mMaxBufferSize;

std::atomic<bool> mHasMutablePass = false;

///@}
///@name Private Operators
///@{
Expand Down Expand Up @@ -1213,7 +1289,7 @@ class PointerVectorSet final
}

///@}
///@name Serialization
///@name Friend classes
///@{

/**
Expand All @@ -1222,6 +1298,19 @@ class PointerVectorSet final
*/
friend class Serializer;

/**
* @class MutablePass
* @brief A friend class responsible for giving a pass to mutate which may destroy the sorted state
* of PointerVectorSet. All methods which relies on the sorted state will be frozen when this is
* active, and they will be unfrozen and PointerVectorSet will be sorted at the destruction on the
* MutablePass.
*/
friend class MutablePass;

///@}
///@name Serialization
///@{

/**
* @brief Extract the object's state and uses the Serializer to store it.
* @param rSerializer Serializer instance to be used for saving.
Expand Down
Loading