Skip to content
Snippets Groups Projects
cow_ptr.h 6.7 KiB
Newer Older
Nick Draper's avatar
Nick Draper committed
#ifndef MANTIDKERNEL_COW_PTR_H
#define MANTIDKERNEL_COW_PTR_H
Stuart Ansell's avatar
Stuart Ansell committed

Nick Draper's avatar
Nick Draper committed
#include "MultiThreaded.h"
#include <boost/shared_ptr.hpp>
Hahn, Steven's avatar
Hahn, Steven committed
#include <boost/make_shared.hpp>
namespace Mantid {
namespace Kernel {
Nick Draper's avatar
Nick Draper committed
  \class cow_ptr
  \brief Implements a copy on write data template
Stuart Ansell's avatar
Stuart Ansell committed
  \version 1.0
  \date February 2006
  \author S.Ansell
Stuart Ansell's avatar
Stuart Ansell committed
  This version works only on data that is created via new().
  It is thread safe and works in the Standard template
  libraries (but appropriate functionals are needed for
Stuart Ansell's avatar
Stuart Ansell committed
  sorting etc.).

  Renamed from RefControl on the 11/12/2007,
  as it was agreed that copy on write pointer better
Nick Draper's avatar
Nick Draper committed
  described the functionality of this class.

Stuart Ansell's avatar
Stuart Ansell committed
  The underlying data can be accessed via the normal pointer
  semantics but call the access function if the data is required
  to be modified.
Nick Draper's avatar
Nick Draper committed

  Copyright &copy; 2007-2010 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge
  National Laboratory & European Spallation Source
Nick Draper's avatar
Nick Draper committed

  This file is part of Mantid.
Nick Draper's avatar
Nick Draper committed
  Mantid is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version.
Nick Draper's avatar
Nick Draper committed
  Mantid is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
Nick Draper's avatar
Nick Draper committed
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  File change history is stored at: <https://github.com/mantidproject/mantid>
Nick Draper's avatar
Nick Draper committed

Stuart Ansell's avatar
Stuart Ansell committed
*/
template <typename DataType> class cow_ptr {
public:
  using ptr_type = boost::shared_ptr<DataType>; ///< typedef for the storage
  using value_type = DataType;                  ///< typedef for the data type
Stuart Ansell's avatar
Stuart Ansell committed

private:
  ptr_type Data; ///< Real object Ptr
Stuart Ansell's avatar
Stuart Ansell committed

  cow_ptr(ptr_type &&resourceSptr) noexcept;
  cow_ptr(const ptr_type &resourceSptr) noexcept;
  explicit cow_ptr(DataType *resourcePtr);
Nick Draper's avatar
Nick Draper committed
  cow_ptr();
  /// Constructs a cow_ptr with no managed object, i.e. empty cow_ptr.
  constexpr cow_ptr(std::nullptr_t) noexcept : Data(nullptr) {}
  cow_ptr(const cow_ptr<DataType> &) noexcept;
  // Move is hand-written, since std::mutex member prevents auto-generation.
  cow_ptr(cow_ptr<DataType> &&other) noexcept : Data(std::move(other.Data)) {}
  cow_ptr<DataType> &operator=(const cow_ptr<DataType> &) noexcept;
  // Move is hand-written, since std::mutex member prevents auto-generation.
  cow_ptr<DataType> &operator=(cow_ptr<DataType> &&rhs) noexcept {
    Data = std::move(rhs.Data);
    return *this;
  }
  cow_ptr<DataType> &operator=(const ptr_type &) noexcept;
Stuart Ansell's avatar
Stuart Ansell committed

  const DataType *get() const noexcept { return Data.get(); }

  /// Checks if *this stores a non-null pointer, i.e. whether get() != nullptr.
  explicit operator bool() const noexcept { return bool(Data); }

  /// Returns the number of different shared_ptr instances (this included)
  /// managing the current object. If there is no managed object, 0 is returned.
  long use_count() const noexcept { return Data.use_count(); }

  /// Checks if *this is the only shared_ptr instance managing the current
  /// object, i.e. whether use_count() == 1.
  bool unique() const noexcept { return Data.unique(); }

  const DataType &operator*() const {
    return *Data;
  } ///< Pointer dereference access
  const DataType *operator->() const {
    return Data.get();
  } ///<indirectrion dereference access
  bool operator==(const cow_ptr<DataType> &A) const noexcept {
    return Data == A.Data;
  } ///< Based on ptr equality
  bool operator!=(const cow_ptr<DataType> &A) const noexcept {
    return Data != A.Data;
  } ///< Based on ptr inequality
  DataType &access();
Stuart Ansell's avatar
Stuart Ansell committed
};

/**
 Constructor : creates a new cow_ptr around the resource
 resource is a sink.
 */
template <typename DataType>
cow_ptr<DataType>::cow_ptr(DataType *resourcePtr)
  Constructor : creates new data() object
*/
template <typename DataType>
cow_ptr<DataType>::cow_ptr()
Hahn, Steven's avatar
Hahn, Steven committed
    : Data(boost::make_shared<DataType>()) {}
Stuart Ansell's avatar
Stuart Ansell committed

/**
  Copy constructor : double references the data object
  @param A :: object to copy
*/
// Note: Need custom implementation, since std::mutex is not copyable.
template <typename DataType>
cow_ptr<DataType>::cow_ptr(const cow_ptr<DataType> &A) noexcept
    : Data(boost::atomic_load(&A.Data)) {}

/**
  Assignment operator : double references the data object
  maybe drops the old reference.
  @param A :: object to copy
  @return *this
*/
// Note: Need custom implementation, since std::mutex is not copyable.
template <typename DataType>
cow_ptr<DataType> &cow_ptr<DataType>::
operator=(const cow_ptr<DataType> &A) noexcept {
    boost::atomic_store(&Data, boost::atomic_load(&A.Data));
  Assignment operator : double references the data object
  maybe drops the old reference.
template <typename DataType>
cow_ptr<DataType> &cow_ptr<DataType>::operator=(const ptr_type &A) noexcept {
  if (this->Data != A) {
    boost::atomic_store(&Data, boost::atomic_load(&A));
Stuart Ansell's avatar
Stuart Ansell committed
  return *this;
}

  Access function.
  If data is shared, creates a copy of Data so that it can be modified.

  In certain situations this function is not thread safe. Specifically it is not
  thread
  safe in the presence of a simultaneous cow_ptr copy. Copies of the underlying
  data are only
  made when the reference count > 1.

template <typename DataType> DataType &cow_ptr<DataType>::access() {
  // Use a double-check for sharing so that we only acquire the lock if
  // absolutely necessary
  if (!Data.unique()) {
    std::lock_guard<std::mutex> lock{copyMutex};
    // Check again because another thread may have taken copy and dropped
    // reference count since previous check
    if (!Data.unique()) {
      boost::atomic_store(&Data, boost::make_shared<DataType>(*Data));
    }
Stuart Ansell's avatar
Stuart Ansell committed
  return *Data;
}

template <typename DataType>
cow_ptr<DataType>::cow_ptr(ptr_type &&resourceSptr) noexcept {
  boost::atomic_store(&this->Data, std::move(resourceSptr));
template <typename DataType>
cow_ptr<DataType>::cow_ptr(const ptr_type &resourceSptr) noexcept {
  boost::atomic_store(&this->Data, boost::atomic_load(&resourceSptr));
Nick Draper's avatar
Nick Draper committed
} // NAMESPACE Kernel

/// typedef for the data storage used in Mantid matrix workspaces
using MantidVec = std::vector<double>;

/// typedef for the pointer to data storage used in Mantid matrix workspaces
using MantidVecPtr = Kernel::cow_ptr<MantidVec>;
Stuart Ansell's avatar
Stuart Ansell committed
} // NAMESPACE Mantid

#endif // MANTIDKERNEL_COW_PTR_H