memgraph/poc/utils/copy_on_write.hpp
Teon Banek 96d9846b25 Remove traces of custom assert
Reviewers: mislav.bradac

Reviewed By: mislav.bradac

Subscribers: pullbot

Differential Revision: https://phabricator.memgraph.io/D905
2017-10-13 09:34:13 +02:00

105 lines
4.1 KiB
C++

#pragma once
#include <memory>
namespace utils {
/**
* Helper class for implementing copy-on-write member variables. Memory
* management is automatic via shared-ptr: it is allowed for the original
* owner to expire, the content will remain valid in copies.
*
* This class is generally not thread-safe. However, it is intended for use in
* Memgraph's MVCC which is concurrent, but with specific guarantees that also
* make it possible to use this. These guarantees are:
* 1. Writing can only happen when there are no copies (no parallel reads). An
* implication of this is that an obtained copy is immutable, even though in
* general CopyOnWrite does not guarantee this (generally the owner's
* modificatins will be visible to the copies).
* 2. Copies are not created in parallel. MVCC guarantees this with
* record-locking, but should be in fact legal in CopyOnWrite.
*
* @tparam TElement - type of content. Must be copy-constructable.
*/
template <typename TElement>
class CopyOnWrite {
public:
/**
* Creates a new CopyOnWrite that owns it's element.
*
* @param args - Arguments forwarded to the TElement constructor.
* @tparam TArgs - Argument types.
*/
template <typename... TArgs>
CopyOnWrite(TArgs &&... args) {
TElement *new_element = new TElement(std::forward<TArgs>(args)...);
element_ptr_.reset(new_element);
is_owner_ = true;
}
/** Creates a copy of the given CopyOnWrite object that does not copy the
* element and does not assume ownership over it. */
CopyOnWrite(const CopyOnWrite &other)
: element_ptr_{other.element_ptr_}, is_owner_{false} {}
/** Creates a copy of the given CopyOnWrite object that does not copy the
* element and does not assume ownership over it. This is a non-const
* reference accepting copy constructor. The hack is necessary to prevent the
* variadic constructor from being a better match for non-const CopyOnWrite
* argument. */
CopyOnWrite(CopyOnWrite &other)
: CopyOnWrite(const_cast<const CopyOnWrite &>(other)) {}
/** Creates a copy of the given temporary CyopOnWrite object. If the temporary
* is owner then ownership is transferred to this CopyOnWrite. Otherwise this
* CopyOnWrite does not become the owner. */
CopyOnWrite(CopyOnWrite &&other) = default;
/** Copy assignment of another CopyOnWrite. Does not transfer ownership (this
* CopyOnWrite is not the owner). */
CopyOnWrite &operator=(const CopyOnWrite &other) {
element_ptr_ = other.element_ptr_;
is_owner_ = false;
return *this;
}
/** Copy assignment of a temporary CopyOnWrite. If the temporary is owner then
* ownerships is transferred to this CopyOnWrite. Otherwise this CopyOnWrite
* does not become the owner. */
CopyOnWrite &operator=(CopyOnWrite &&other) = default;
// All the dereferencing operators are overloaded to return a const element
// reference. There is no differentiation between const and non-const member
// function behavior because an implicit copy creation on non-const
// dereferencing would most likely result in excessive copying. For that
// reason
// an explicit call to the `Write` function is required to obtain a non-const
// reference to element.
const TElement &operator*() { return *element_ptr_; }
const TElement &operator*() const { return *element_ptr_; }
const TElement &get() { return *element_ptr_; }
const TElement &get() const { return *element_ptr_; }
const TElement *operator->() { return element_ptr_.get(); }
const TElement *operator->() const { return element_ptr_.get(); }
/** Indicates if this CopyOnWrite object is the owner of it's element. */
bool is_owner() const { return is_owner_; };
/**
* If this CopyOnWrite is the owner of it's element, a non-const reference to
* is returned. If this CopyOnWrite is not the owner, then the element is
* copied and this CopyOnWrite becomes the owner.
*/
TElement &Write() {
if (is_owner_) return *element_ptr_;
element_ptr_ = std::shared_ptr<TElement>(new TElement(*element_ptr_));
is_owner_ = true;
return *element_ptr_;
};
private:
std::shared_ptr<TElement> element_ptr_;
bool is_owner_{false};
};
} // namespace utils