What I think could work here is own implementation of vector
, something like this:
template <typename T> class Vector
{
// constructor will be needed of course
public:
std::shared_ptr<const std::vector<T> > getVector()
{ return mVector; }
void push_back(const T&);
private:
std::shared_ptr<std::vector<T> > mVector;
};
Then, whenever readers need to access a specific Vector
, they should call getVector()
and keep the returned shared_ptr
until finished reading.
But writers should always use Vector
's push_back
to add new value. This push_back
should then check if mVector.size() == mVector.capacity()
and if true, allocate new vector
and assign it to mVector
. Something like:
template <typename T> Vector<T>::push_back(const T& t)
{
if (mVector->size() == mVector->capacity())
{
// make certain here that new_size > old_size
std::vector<T> vec = new std::vector<T> (mVector->size() * SIZE_MULTIPLIER);
std::copy(mVector->begin(), mVector->end(), vec->begin());
mVector.reset(vec);
}
// put 't' into 'mVector'. 'mVector' is guaranteed not to reallocate now.
}
The idea here is inspired by RCU (read-copy-update) algorithm. If storage space is exhausted, the new storage should not invalidate the old storage as long as there is at least one reader accessing it. But, the new storage should be allocated and any reader coming after allocation, should be able to see it. The old storage should be deallocated as soon as no one is using it anymore (all readers are finished).
Since most HW architectures provide some way to have atomic increments and decrements, I think shared_ptr
(and thus Vector
) will be able to run completely lock-less.
One disadvantage to this approach though, is that depending on how long readers hold that shared_ptr
you might end up with several copies of your data
.
PS: hope I haven't made too many embarrassing errors in the code :-)