Commit c1237969 authored by Robert Maynard's avatar Robert Maynard

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));
```
parent 707970f4
......@@ -303,6 +303,13 @@ public:
///
ArrayHandle(const StorageType& storage);
/// Special constructor for subclass specializations that need to set the
/// initial state of the control array. When this constructor is used, it
/// is assumed that the control array is valid.
///
ArrayHandle(StorageType&& storage);
/// Destructs an empty ArrayHandle.
///
/// Implemented so that it is defined exclusively in the control environment.
......
......@@ -52,6 +52,15 @@ ArrayHandle<T, S>::ArrayHandle(const typename ArrayHandle<T, S>::StorageType& st
this->Internals->ExecutionArrayValid = false;
}
template <typename T, typename S>
ArrayHandle<T, S>::ArrayHandle(typename ArrayHandle<T, S>::StorageType&& storage)
: Internals(new InternalStruct)
{
this->Internals->ControlArray = std::move(storage);
this->Internals->ControlArrayValid = true;
this->Internals->ExecutionArrayValid = false;
}
template <typename T, typename S>
ArrayHandle<T, S>::~ArrayHandle()
{
......
......@@ -117,6 +117,19 @@ StorageBasicBase::~StorageBasicBase()
this->ReleaseResources();
}
StorageBasicBase::StorageBasicBase(StorageBasicBase&& src)
: Array(src.Array)
, AllocatedByteSize(src.AllocatedByteSize)
, NumberOfValues(src.NumberOfValues)
, DeleteFunction(src.DeleteFunction)
{
src.Array = nullptr;
src.AllocatedByteSize = 0;
src.NumberOfValues = 0;
src.DeleteFunction = nullptr;
}
StorageBasicBase::StorageBasicBase(const StorageBasicBase& src)
: Array(src.Array)
, AllocatedByteSize(src.AllocatedByteSize)
......@@ -132,6 +145,21 @@ StorageBasicBase::StorageBasicBase(const StorageBasicBase& src)
}
}
StorageBasicBase StorageBasicBase::operator=(StorageBasicBase&& src)
{
this->ReleaseResources();
this->Array = src.Array;
this->AllocatedByteSize = src.AllocatedByteSize;
this->NumberOfValues = src.NumberOfValues;
this->DeleteFunction = src.DeleteFunction;
src.Array = nullptr;
src.AllocatedByteSize = 0;
src.NumberOfValues = 0;
src.DeleteFunction = nullptr;
return *this;
}
StorageBasicBase StorageBasicBase::operator=(const StorageBasicBase& src)
{
if (src.DeleteFunction)
......
......@@ -83,7 +83,9 @@ public:
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).
......
......@@ -19,6 +19,7 @@
##============================================================================
set(unit_tests
UnitTestCudaArrayHandle.cu
UnitTestCudaArrayHandleFancy.cu
UnitTestCudaArrayHandleVirtualCoordinates.cu
UnitTestCudaCellLocatorTwoLevelUniformGrid.cu
......
//============================================================================
// 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.
//============================================================================
#define VTKM_DEVICE_ADAPTER VTKM_DEVICE_ADAPTER_ERROR
#include <vtkm/cont/cuda/DeviceAdapterCuda.h>
#include <vtkm/cont/testing/TestingArrayHandles.h>
int UnitTestCudaArrayHandle(int, char* [])
{
return vtkm::cont::testing::TestingArrayHandles<vtkm::cont::DeviceAdapterTagCuda>::Run();
}
......@@ -156,6 +156,20 @@ struct VTKM_CONT_EXPORT ArrayHandleImpl
{
}
VTKM_CONT
template <typename T>
explicit ArrayHandleImpl(vtkm::cont::internal::Storage<T, vtkm::cont::StorageTagBasic>&& storage)
: ControlArrayValid(true)
, ControlArray(
new vtkm::cont::internal::Storage<T, vtkm::cont::StorageTagBasic>(std::move(storage)))
, ExecutionInterface(nullptr)
, ExecutionArrayValid(false)
, ExecutionArray(nullptr)
, ExecutionArrayEnd(nullptr)
, ExecutionArrayCapacity(nullptr)
{
}
VTKM_CONT ~ArrayHandleImpl();
VTKM_CONT ArrayHandleImpl(const ArrayHandleImpl&) = delete;
......@@ -225,8 +239,10 @@ public:
VTKM_CONT ArrayHandle();
VTKM_CONT ArrayHandle(const Thisclass& src);
VTKM_CONT ArrayHandle(const Thisclass&& src);
VTKM_CONT ArrayHandle(Thisclass&& src);
VTKM_CONT ArrayHandle(const StorageType& storage);
VTKM_CONT ArrayHandle(StorageType&& storage);
VTKM_CONT ~ArrayHandle();
......
......@@ -40,7 +40,7 @@ ArrayHandle<T, StorageTagBasic>::ArrayHandle(const Thisclass& src)
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle(const Thisclass&& src)
ArrayHandle<T, StorageTagBasic>::ArrayHandle(Thisclass&& src)
: Internals(std::move(src.Internals))
{
}
......@@ -51,6 +51,12 @@ ArrayHandle<T, StorageTagBasic>::ArrayHandle(const StorageType& storage)
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::ArrayHandle(StorageType&& storage)
: Internals(new internal::ArrayHandleImpl(std::move(storage)))
{
}
template <typename T>
ArrayHandle<T, StorageTagBasic>::~ArrayHandle()
{
......
......@@ -166,7 +166,7 @@ private:
}
};
struct VerifyUserAllocatedHandle
struct VerifyUserOwnedMemory
{
template <typename T>
VTKM_CONT void operator()(T) const
......@@ -241,6 +241,85 @@ private:
}
};
struct VerifyUserTransferredMemory
{
template <typename T>
VTKM_CONT void operator()(T) const
{
T* buffer = new T[ARRAY_SIZE];
for (vtkm::Id index = 0; index < ARRAY_SIZE; index++)
{
buffer[static_cast<std::size_t>(index)] = TestValue(index, T());
}
auto user_free_function = [](void* ptr) { delete[] static_cast<T*>(ptr); };
vtkm::cont::internal::Storage<T, vtkm::cont::StorageTagBasic> storage(
buffer, ARRAY_SIZE, user_free_function);
vtkm::cont::ArrayHandle<T> arrayHandle(std::move(storage));
VTKM_TEST_ASSERT(arrayHandle.GetNumberOfValues() == ARRAY_SIZE,
"ArrayHandle has wrong number of entries.");
std::cout << "Check array with user transferred memory." << std::endl;
array_handle_testing::CheckArray(arrayHandle);
std::cout << "Check out execution array behavior." << std::endl;
{ //as input
typename vtkm::cont::ArrayHandle<T>::template ExecutionTypes<DeviceAdapterTag>::PortalConst
executionPortal;
executionPortal = arrayHandle.PrepareForInput(DeviceAdapterTag());
//use a worklet to verify the input transfer worked properly
vtkm::cont::ArrayHandle<T> result;
DispatcherPassThrough().Invoke(arrayHandle, result);
array_handle_testing::CheckArray(result);
}
std::cout << "Check out inplace." << std::endl;
{ //as inplace
typename vtkm::cont::ArrayHandle<T>::template ExecutionTypes<DeviceAdapterTag>::Portal
executionPortal;
executionPortal = arrayHandle.PrepareForInPlace(DeviceAdapterTag());
//use a worklet to verify the inplace transfer worked properly
vtkm::cont::ArrayHandle<T> result;
DispatcherPassThrough().Invoke(arrayHandle, result);
array_handle_testing::CheckArray(result);
}
std::cout << "Check out output." << std::endl;
{ //as output with same length as user provided. This should work
//as no new memory needs to be allocated
typename vtkm::cont::ArrayHandle<T>::template ExecutionTypes<DeviceAdapterTag>::Portal
executionPortal;
executionPortal = arrayHandle.PrepareForOutput(ARRAY_SIZE, DeviceAdapterTag());
//we can't verify output contents as those aren't fetched, we
//can just make sure the allocation didn't throw an exception
}
{ //as the memory ownership has been transferred to VTK-m this should
//allow VTK-m to free the memory and allocate a new block
bool gotException = false;
try
{
//you should not be able to allocate a size larger than the
//user provided and get the results
arrayHandle.PrepareForOutput(ARRAY_SIZE * 2, DeviceAdapterTag());
arrayHandle.GetPortalControl();
}
catch (vtkm::cont::Error&)
{
gotException = true;
}
VTKM_TEST_ASSERT(!gotException,
"PrepareForOutput shouldn't fail when asked to "
"re-allocate user transferred memory.");
}
}
};
struct VerifyVTKMAllocatedHandle
{
template <typename T>
......@@ -369,7 +448,8 @@ private:
void operator()() const
{
vtkm::testing::Testing::TryTypes(VerifyEmptyArrays());
vtkm::testing::Testing::TryTypes(VerifyUserAllocatedHandle());
vtkm::testing::Testing::TryTypes(VerifyUserOwnedMemory());
vtkm::testing::Testing::TryTypes(VerifyUserTransferredMemory());
vtkm::testing::Testing::TryTypes(VerifyVTKMAllocatedHandle());
vtkm::testing::Testing::TryTypes(VerifyEqualityOperators());
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment