vtk-m2/vtkm/cont/testing/UnitTestArrayHandleVirtual.cxx
Kenneth Moreland 10e8a4a7f9 Remove locking control ArrayPortals
Previously, when a ReadPortal or a WritePortal was returned from an
ArrayHandle, it had wrapped in it a Token that was attached to the
ArrayHandle. This Token would prevent other reads and writes from the
ArrayHandle.

This added safety in the form of making sure that the ArrayPortal was
always valid. Unfortunately, it also made deadlocks very easy. They
happened when an ArrayPortal did not leave scope immediately after use
(which is not all that uncommon).

Now, the ArrayPortal no longer locks up the ArrayHandle. Instead, when
an access happens on the ArrayPortal, it checks to make sure that
nothing has happened to the data being accessed. If it has, a fatal
error is reported to the log.
2020-03-16 07:10:10 -06:00

253 lines
8.2 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.
//============================================================================
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/cont/ArrayHandleVirtual.h>
#include <vtkm/cont/DeviceAdapterAlgorithm.h>
#include <vtkm/cont/serial/internal/ArrayManagerExecutionSerial.h>
#include <vtkm/cont/serial/internal/DeviceAdapterAlgorithmSerial.h>
#include <vtkm/cont/serial/internal/DeviceAdapterTagSerial.h>
#include <vtkm/cont/testing/Testing.h>
#include <vtkm/BinaryOperators.h>
#include <algorithm>
namespace UnitTestArrayHandleVirtualDetail
{
template <typename ValueType>
struct Test
{
static constexpr vtkm::Id ARRAY_SIZE = 100;
static constexpr vtkm::Id NUM_KEYS = 3;
using ArrayHandle = vtkm::cont::ArrayHandle<ValueType>;
using VirtHandle = vtkm::cont::ArrayHandleVirtual<ValueType>;
using DeviceTag = vtkm::cont::DeviceAdapterTagSerial;
using Algorithm = vtkm::cont::DeviceAdapterAlgorithm<DeviceTag>;
void TestConstructors()
{
std::cout << "Constructors" << std::endl;
VirtHandle nullStorage;
VTKM_TEST_ASSERT(nullStorage.GetStorage().GetStorageVirtual() == nullptr,
"storage should be empty when using ArrayHandleVirtual().");
VirtHandle fromArrayHandle{ ArrayHandle{} };
VTKM_TEST_ASSERT(fromArrayHandle.GetStorage().GetStorageVirtual() != nullptr,
"storage should be empty when using ArrayHandleVirtual().");
VTKM_TEST_ASSERT(vtkm::cont::IsType<ArrayHandle>(fromArrayHandle),
"ArrayHandleVirtual should contain a ArrayHandle<ValueType>.");
VirtHandle fromVirtHandle(fromArrayHandle);
VTKM_TEST_ASSERT(fromVirtHandle.GetStorage().GetStorageVirtual() != nullptr,
"storage should be empty when using ArrayHandleVirtual().");
VTKM_TEST_ASSERT(vtkm::cont::IsType<ArrayHandle>(fromVirtHandle),
"ArrayHandleVirtual should contain a ArrayHandle<ValueType>.");
VirtHandle fromNullPtrHandle(nullStorage);
VTKM_TEST_ASSERT(fromNullPtrHandle.GetStorage().GetStorageVirtual() == nullptr,
"storage should be empty when constructing from a ArrayHandleVirtual that has "
"nullptr storage.");
VTKM_TEST_ASSERT((vtkm::cont::IsType<ArrayHandle>(fromNullPtrHandle) == false),
"ArrayHandleVirtual shouldn't match any type with nullptr storage.");
}
void TestMoveConstructors()
{
std::cout << "Move constructors" << std::endl;
//test ArrayHandle move constructor
{
ArrayHandle handle;
VirtHandle virt(std::move(handle));
VTKM_TEST_ASSERT(
vtkm::cont::IsType<ArrayHandle>(virt),
"ArrayHandleVirtual should be valid after move constructor ArrayHandle<ValueType>.");
}
//test ArrayHandleVirtual move constructor
{
ArrayHandle handle;
VirtHandle virt(std::move(handle));
VirtHandle virt2(std::move(virt));
VTKM_TEST_ASSERT(
vtkm::cont::IsType<ArrayHandle>(virt2),
"ArrayHandleVirtual should be valid after move constructor ArrayHandleVirtual<ValueType>.");
}
}
void TestAssignmentOps()
{
std::cout << "Assignment operators" << std::endl;
//test assignment operator from ArrayHandleVirtual
{
VirtHandle virt;
virt = VirtHandle{ ArrayHandle{} };
VTKM_TEST_ASSERT(vtkm::cont::IsType<ArrayHandle>(virt),
"ArrayHandleVirtual should be valid after assignment op from AHV.");
}
//test assignment operator from ArrayHandle
{
VirtHandle virt = vtkm::cont::ArrayHandleCounting<ValueType>{};
virt = ArrayHandle{};
VTKM_TEST_ASSERT(vtkm::cont::IsType<ArrayHandle>(virt),
"ArrayHandleVirtual should be valid after assignment op from AH.");
}
//test move assignment operator from ArrayHandleVirtual
{
VirtHandle temp{ ArrayHandle{} };
VirtHandle virt;
virt = std::move(temp);
VTKM_TEST_ASSERT(vtkm::cont::IsType<ArrayHandle>(virt),
"ArrayHandleVirtual should be valid after move assignment op from AHV.");
}
//test move assignment operator from ArrayHandle
{
vtkm::cont::ArrayHandleCounting<ValueType> temp;
VirtHandle virt;
virt = std::move(temp);
VTKM_TEST_ASSERT(vtkm::cont::IsType<decltype(temp)>(virt),
"ArrayHandleVirtual should be valid after move assignment op from AH.");
}
}
void TestPrepareForExecution()
{
std::cout << "Prepare for execution" << std::endl;
vtkm::cont::ArrayHandle<ValueType> handle;
handle.Allocate(ARRAY_SIZE);
VirtHandle virt(std::move(handle));
try
{
vtkm::cont::Token token;
virt.PrepareForInput(DeviceTag(), token);
virt.PrepareForInPlace(DeviceTag(), token);
virt.PrepareForOutput(ARRAY_SIZE, DeviceTag(), token);
}
catch (vtkm::cont::ErrorBadValue&)
{
// un-expected failure.
VTKM_TEST_FAIL(
"Unexpected error when using Prepare* on an ArrayHandleVirtual with StorageAny.");
}
}
void TestIsType()
{
std::cout << "IsType" << std::endl;
vtkm::cont::ArrayHandle<ValueType> handle;
VirtHandle virt(std::move(handle));
VTKM_TEST_ASSERT(vtkm::cont::IsType<decltype(virt)>(virt),
"virt should by same type as decltype(virt)");
VTKM_TEST_ASSERT(vtkm::cont::IsType<decltype(handle)>(virt),
"virt should by same type as decltype(handle)");
vtkm::cont::ArrayHandle<vtkm::Vec<ValueType, 3>> vecHandle;
VTKM_TEST_ASSERT(!vtkm::cont::IsType<decltype(vecHandle)>(virt),
"virt shouldn't by same type as decltype(vecHandle)");
}
void TestCast()
{
std::cout << "Cast" << std::endl;
vtkm::cont::ArrayHandle<ValueType> handle;
VirtHandle virt(handle);
auto c1 = vtkm::cont::Cast<decltype(virt)>(virt);
VTKM_TEST_ASSERT(c1 == virt, "virt should cast to VirtHandle");
auto c2 = vtkm::cont::Cast<decltype(handle)>(virt);
VTKM_TEST_ASSERT(c2 == handle, "virt should cast to HandleType");
using VecHandle = vtkm::cont::ArrayHandle<vtkm::Vec<ValueType, 3>>;
try
{
auto c3 = vtkm::cont::Cast<VecHandle>(virt);
VTKM_TEST_FAIL("Cast of T to Vec<T,3> should have failed");
}
catch (vtkm::cont::ErrorBadType&)
{
}
}
void TestControlPortalLocking()
{
std::cout << "Control portal locking" << std::endl;
// There was a bug where a control portal was not relinquished and it locked the
// ArrayHandle from further use.
ArrayHandle concreteArray;
concreteArray.Allocate(ARRAY_SIZE);
VirtHandle virtualArray(concreteArray);
// Make sure you can write to the virtualArray and then read the data from the concreteArray
// without the concreteArray getting locked up.
SetPortal(virtualArray.WritePortal());
CheckPortal(concreteArray.ReadPortal());
// Make sure you can read from the virtualArray and the write to the concreteArray without
// the concreteArray getting locked up.
CheckPortal(virtualArray.ReadPortal());
SetPortal(concreteArray.WritePortal());
}
void operator()()
{
std::cout << std::endl;
std::cout << "### Testing for " << vtkm::cont::TypeToString<ValueType>() << std::endl;
TestConstructors();
TestMoveConstructors();
TestAssignmentOps();
TestPrepareForExecution();
TestIsType();
TestCast();
TestControlPortalLocking();
}
};
void TestArrayHandleVirtual()
{
Test<vtkm::UInt8>()();
Test<vtkm::Int16>()();
Test<vtkm::Int32>()();
Test<vtkm::Int64>()();
Test<vtkm::Float32>()();
Test<vtkm::Float64>()();
}
} // end namespace UnitTestArrayHandleVirtualDetail
int UnitTestArrayHandleVirtual(int argc, char* argv[])
{
using namespace UnitTestArrayHandleVirtualDetail;
return vtkm::cont::testing::Testing::Run(TestArrayHandleVirtual, argc, argv);
}