vtk-m/vtkm/cont/StorageBasic.h
Robert Maynard c123796949 VTK-m ArrayHandle can now take ownership of a user allocated memory location
Previously memory that was allocated outside of VTK-m was impossible to transfer to
VTK-m as we didn't know how to free it. By extending the ArrayHandle constructors
to support a Storage object that is being moved, we can clearly express that
the ArrayHandle now owns memory it didn't allocate.

Here is an example of how this is done:
```cpp
  T* buffer = new T[100];
  auto user_free_function = [](void* ptr) { delete[] static_cast<T*>(ptr); };

  vtkm::cont::internal::Storage<T, vtkm::cont::StorageTagBasic>
      storage(buffer, 100, user_free_function);
  vtkm::cont::ArrayHandle<T> arrayHandle(std::move(storage));
```
2018-04-04 11:28:25 -04:00

258 lines
9.6 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//
// Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2014 UT-Battelle, LLC.
// Copyright 2014 Los Alamos National Security.
//
// Under the terms of Contract DE-NA0003525 with NTESS,
// the U.S. Government retains certain rights in this software.
//
// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
// Laboratory (LANL), the U.S. Government retains certain rights in
// this software.
//============================================================================
#ifndef vtk_m_cont_StorageBasic_h
#define vtk_m_cont_StorageBasic_h
#include <vtkm/Assert.h>
#include <vtkm/Types.h>
#include <vtkm/cont/ErrorBadAllocation.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/Storage.h>
#include <vtkm/cont/internal/ArrayPortalFromIterators.h>
namespace vtkm
{
namespace cont
{
/// A tag for the basic implementation of a Storage object.
struct VTKM_ALWAYS_EXPORT StorageTagBasic
{
};
namespace internal
{
/// Function that does all of VTK-m de-allocations for storage basic.
/// This is exists so that stolen arrays can call the correct free
/// function ( _aligned_malloc / cuda_free ).
VTKM_CONT_EXPORT void free_memory(void* ptr);
/// Class that does all of VTK-m allocations
/// for storage basic. This is exists so that
/// stolen arrays can call the correct free
/// function ( _aligned_malloc ) on windows
struct VTKM_CONT_EXPORT StorageBasicAllocator
{
void* allocate(size_t size, size_t align);
template <typename T>
inline void deallocate(T* p)
{
internal::free_memory(static_cast<void*>(p));
}
};
/// Base class for basic storage classes. This allow us to implement
/// vtkm::cont::Storage<T, StorageTagBasic > for any T type with no overhead
/// as all heavy logic is provide by a type-agnostic API including allocations, etc.
class VTKM_CONT_EXPORT StorageBasicBase
{
public:
using AllocatorType = StorageBasicAllocator;
VTKM_CONT StorageBasicBase();
/// A non owning view of already allocated memory
VTKM_CONT StorageBasicBase(const void* array, vtkm::Id size, vtkm::UInt64 sizeOfValue);
/// Transfer the ownership of already allocated memory to VTK-m
VTKM_CONT StorageBasicBase(const void* array,
vtkm::Id size,
vtkm::UInt64 sizeOfValue,
void (*deleteFunction)(void*));
VTKM_CONT ~StorageBasicBase();
VTKM_CONT StorageBasicBase(StorageBasicBase&& src);
VTKM_CONT StorageBasicBase(const StorageBasicBase& src);
VTKM_CONT StorageBasicBase operator=(StorageBasicBase&& src);
VTKM_CONT StorageBasicBase operator=(const StorageBasicBase& src);
/// \brief Return the number of bytes allocated for this storage object(Capacity).
///
///
VTKM_CONT vtkm::UInt64 GetNumberOfBytes() const { return this->AllocatedByteSize; }
/// \brief Return the number of 'T' values allocated by this storage
VTKM_CONT vtkm::Id GetNumberOfValues() const { return this->NumberOfValues; }
/// \brief Allocates an array with the specified number of elements.
///
/// The allocation may be done on an already existing array, but can wipe out
/// any data already in the array. This method can throw
/// ErrorBadAllocation if the array cannot be allocated or
/// ErrorBadValue if the allocation is not feasible (for example, the
/// array storage is read-only).
VTKM_CONT void AllocateValues(vtkm::Id numberOfValues, vtkm::UInt64 sizeOfValue);
/// \brief Reduces the size of the array without changing its values.
///
/// This method allows you to resize the array without reallocating it. The
/// size of the array is changed so that it can hold \c numberOfValues values.
/// The data in the reallocated array stays the same, but \c numberOfValues must be
/// equal or less than the preexisting size. That is, this method can only be
/// used to shorten the array, not lengthen.
VTKM_CONT void Shrink(vtkm::Id numberOfValues);
/// \brief Frees any resources (i.e. memory) stored in this array.
///
/// After calling this method GetNumberOfBytes() will return 0. The
/// resources should also be released when the Storage class is
/// destroyed.
VTKM_CONT void ReleaseResources();
/// \brief Returns if vtkm will deallocate this memory. VTK-m StorageBasic
/// is designed that VTK-m will not deallocate user passed memory, or
/// instances that have been stolen (\c StealArray)
VTKM_CONT bool WillDeallocate() const { return this->DeleteFunction != nullptr; }
/// \brief Return the free function that will be used to free this memory.
///
/// Get the function that VTK-m will call to deallocate the memory. This
/// is useful when stealing memory from VTK-m so that you call the correct
/// free/delete function when releasing the memory
using DeleteFunctionSignature = void (*)(void*);
DeleteFunctionSignature GetDeleteFunction() const { return this->DeleteFunction; }
/// \brief Change the Change the pointer that this class is using. Should only be used
/// by ExecutionArrayInterface sublcasses.
/// Note: This will release any previous allocated memory!
VTKM_CONT void SetBasePointer(const void* ptr,
vtkm::Id numberOfValues,
vtkm::UInt64 sizeOfValue,
void (*deleteFunction)(void*));
/// Return the memory location of the first element of the array data.
VTKM_CONT void* GetBasePointer() const;
VTKM_CONT void* GetEndPointer(vtkm::Id numberOfValues, vtkm::UInt64 sizeOfValue) const;
/// Return the memory location of the first element past the end of the
/// array's allocated memory buffer.
VTKM_CONT void* GetCapacityPointer() const;
protected:
void* Array;
vtkm::UInt64 AllocatedByteSize;
vtkm::Id NumberOfValues;
void (*DeleteFunction)(void*);
};
/// A basic implementation of an Storage object.
///
/// \todo This storage does \em not construct the values within the array.
/// Thus, it is important to not use this class with any type that will fail if
/// not constructed. These are things like basic types (int, float, etc.) and
/// the VTKm Tuple classes. In the future it would be nice to have a compile
/// time check to enforce this.
///
template <typename ValueT>
class VTKM_ALWAYS_EXPORT Storage<ValueT, vtkm::cont::StorageTagBasic> : public StorageBasicBase
{
public:
using AllocatorType = vtkm::cont::internal::StorageBasicAllocator;
using ValueType = ValueT;
using PortalType = vtkm::cont::internal::ArrayPortalFromIterators<ValueType*>;
using PortalConstType = vtkm::cont::internal::ArrayPortalFromIterators<const ValueType*>;
public:
/// \brief construct storage that VTK-m is responsible for
VTKM_CONT Storage();
/// \brief construct storage that VTK-m is not responsible for
VTKM_CONT Storage(const ValueType* array, vtkm::Id numberOfValues);
/// \brief construct storage that was previously allocated and now VTK-m is
/// responsible for
VTKM_CONT Storage(const ValueType* array, vtkm::Id numberOfValues, void (*deleteFunction)(void*));
VTKM_CONT void Allocate(vtkm::Id numberOfValues);
VTKM_CONT PortalType GetPortal();
VTKM_CONT PortalConstType GetPortalConst() const;
/// \brief Get a pointer to the underlying data structure.
///
/// This method returns the pointer to the array held by this array. The
/// memory associated with this array still belongs to the Storage (i.e.
/// Storage will eventually deallocate the array).
///
VTKM_CONT ValueType* GetArray();
VTKM_CONT const ValueType* GetArray() const;
/// \brief Take the reference away from this object.
///
/// This method returns the pointer to the array held by this array. It then
/// clears the internal ownership flags, thereby ensuring that the
/// Storage will never deallocate the array or be able to reallocate it. This
/// is helpful for taking a reference for an array created internally by
/// VTK-m and not having to keep a VTK-m object around. Obviously the caller
/// becomes responsible for destroying the memory, and should before hand
/// grab the correct DeleteFunction by calling \c GetDeleteFunction
///
VTKM_CONT ValueType* StealArray();
};
} // namespace internal
}
} // namespace vtkm::cont
#ifndef vtkm_cont_StorageBasic_cxx
namespace vtkm
{
namespace cont
{
namespace internal
{
/// \cond
/// Make doxygen ignore this section
#define _VTKM_STORAGE_EXPORT(Type) \
extern template class VTKM_CONT_TEMPLATE_EXPORT Storage<Type, StorageTagBasic>; \
extern template class VTKM_CONT_TEMPLATE_EXPORT Storage<vtkm::Vec<Type, 2>, StorageTagBasic>; \
extern template class VTKM_CONT_TEMPLATE_EXPORT Storage<vtkm::Vec<Type, 3>, StorageTagBasic>; \
extern template class VTKM_CONT_TEMPLATE_EXPORT Storage<vtkm::Vec<Type, 4>, StorageTagBasic>;
_VTKM_STORAGE_EXPORT(char)
_VTKM_STORAGE_EXPORT(vtkm::Int8)
_VTKM_STORAGE_EXPORT(vtkm::UInt8)
_VTKM_STORAGE_EXPORT(vtkm::Int16)
_VTKM_STORAGE_EXPORT(vtkm::UInt16)
_VTKM_STORAGE_EXPORT(vtkm::Int32)
_VTKM_STORAGE_EXPORT(vtkm::UInt32)
_VTKM_STORAGE_EXPORT(vtkm::Int64)
_VTKM_STORAGE_EXPORT(vtkm::UInt64)
_VTKM_STORAGE_EXPORT(vtkm::Float32)
_VTKM_STORAGE_EXPORT(vtkm::Float64)
/// \endcond
}
}
}
#endif
#include <vtkm/cont/StorageBasic.hxx>
#endif //vtk_m_cont_StorageBasic_h