//============================================================================ // 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 #include #include #include #include #include #include #include #include #include #include namespace { constexpr vtkm::Id ARRAY_SIZE = 100; template struct AtomicTests { vtkm::cont::Invoker Invoke; static constexpr vtkm::Id OVERLAP = sizeof(T) * CHAR_BIT; static constexpr vtkm::Id EXTENDED_SIZE = ARRAY_SIZE * OVERLAP; VTKM_EXEC_CONT static T TestValue(vtkm::Id index) { return ::TestValue(index, T{}); } struct ArrayToRawPointer : vtkm::cont::ExecutionObjectBase { vtkm::cont::ArrayHandleBasic Array; VTKM_CONT ArrayToRawPointer(const vtkm::cont::ArrayHandleBasic& array) : Array(array) { } VTKM_CONT T* PrepareForExecution(vtkm::cont::DeviceAdapterId device, vtkm::cont::Token& token) const { return reinterpret_cast(this->Array.GetBuffers()[0].WritePointerDevice(device, token)); } }; struct LoadFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { if (!test_equal(vtkm::AtomicLoad(data + index), TestValue(index))) { this->RaiseError("Bad AtomicLoad"); } } }; VTKM_CONT void TestLoad() { std::cout << "AtomicLoad" << std::endl; vtkm::cont::ArrayHandleBasic array; array.Allocate(ARRAY_SIZE); SetPortal(array.WritePortal()); this->Invoke(LoadFunctor{}, array, ArrayToRawPointer(array)); } struct StoreFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::AtomicStore(data + (index % ARRAY_SIZE), TestValue(index)); } }; VTKM_CONT void TestStore() { std::cout << "AtomicStore" << std::endl; vtkm::cont::ArrayHandleBasic array; array.Allocate(ARRAY_SIZE); this->Invoke( StoreFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { bool foundExpected = false; T foundValue = portal.Get(arrayIndex); for (vtkm::Id overlapIndex = 0; overlapIndex < OVERLAP; ++overlapIndex) { if (test_equal(foundValue, TestValue(arrayIndex + (overlapIndex * ARRAY_SIZE)))) { foundExpected = true; break; } } VTKM_TEST_ASSERT( foundExpected, "Wrong value (", foundValue, ") stored in index ", arrayIndex); } } struct AddFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::AtomicAdd(data + (index % ARRAY_SIZE), 2); vtkm::AtomicAdd(data + (index % ARRAY_SIZE), -1); } }; VTKM_CONT void TestAdd() { std::cout << "AtomicAdd" << std::endl; vtkm::cont::ArrayHandleBasic array; vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(0, ARRAY_SIZE), array); array.Allocate(ARRAY_SIZE); this->Invoke( AddFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); T expectedValue = T(OVERLAP); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { T foundValue = portal.Get(arrayIndex); VTKM_TEST_ASSERT(test_equal(foundValue, expectedValue), foundValue, " != ", expectedValue); } } struct AndFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::Id arrayIndex = index % ARRAY_SIZE; vtkm::Id offsetIndex = index / ARRAY_SIZE; vtkm::AtomicAnd(data + arrayIndex, ~(0x1u << offsetIndex)); } }; VTKM_CONT void TestAnd() { std::cout << "AtomicAnd" << std::endl; vtkm::cont::ArrayHandleBasic array; vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(T(-1), ARRAY_SIZE), array); array.Allocate(ARRAY_SIZE); this->Invoke( AndFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { T foundValue = portal.Get(arrayIndex); VTKM_TEST_ASSERT(test_equal(foundValue, 0), foundValue, " != 0"); } } struct OrFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::Id arrayIndex = index % ARRAY_SIZE; vtkm::Id offsetIndex = index / ARRAY_SIZE; vtkm::AtomicOr(data + arrayIndex, 0x1u << offsetIndex); } }; VTKM_CONT void TestOr() { std::cout << "AtomicOr" << std::endl; vtkm::cont::ArrayHandleBasic array; vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(0, ARRAY_SIZE), array); array.Allocate(ARRAY_SIZE); this->Invoke( AndFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); T expectedValue = T(-1); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { T foundValue = portal.Get(arrayIndex); VTKM_TEST_ASSERT(test_equal(foundValue, 0), foundValue, " != ", expectedValue); } } struct XorFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::Id arrayIndex = index % ARRAY_SIZE; vtkm::Id offsetIndex = index / ARRAY_SIZE; vtkm::AtomicXor(data + arrayIndex, 0x3u << offsetIndex); } }; VTKM_CONT void TestXor() { std::cout << "AtomicXor" << std::endl; vtkm::cont::ArrayHandleBasic array; vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(0, ARRAY_SIZE), array); array.Allocate(ARRAY_SIZE); this->Invoke( AndFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); T expectedValue = T(1); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { T foundValue = portal.Get(arrayIndex); VTKM_TEST_ASSERT(test_equal(foundValue, 0), foundValue, " != ", expectedValue); } } struct NotFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::Id arrayIndex = index % ARRAY_SIZE; vtkm::Id offsetIndex = index / ARRAY_SIZE; if (offsetIndex < arrayIndex) { vtkm::AtomicNot(data + arrayIndex); } } }; VTKM_CONT void TestNot() { std::cout << "AtomicNot" << std::endl; vtkm::cont::ArrayHandleBasic array; vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(0xA, ARRAY_SIZE), array); array.Allocate(ARRAY_SIZE); this->Invoke( AndFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); T expectedValue = T(0xA); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { T foundValue = portal.Get(arrayIndex); VTKM_TEST_ASSERT(test_equal(foundValue, 0), foundValue, " != ", expectedValue); expectedValue = static_cast(~expectedValue); } } struct CompareAndSwapFunctor : vtkm::worklet::WorkletMapField { using ControlSignature = void(FieldIn ignored, ExecObject); using ExecutionSignature = void(WorkIndex, _2); VTKM_EXEC void operator()(vtkm::Id index, T* data) const { vtkm::Id arrayIndex = index % ARRAY_SIZE; bool success = false; for (T overlapIndex = 0; overlapIndex < static_cast(OVERLAP); ++overlapIndex) { T oldValue = vtkm::AtomicCompareAndSwap(data + arrayIndex, overlapIndex + 1, overlapIndex); if (oldValue == overlapIndex) { success = true; break; } } if (!success) { this->RaiseError("No compare succeeded"); } } }; VTKM_CONT void TestCompareAndSwap() { std::cout << "AtomicCompareAndSwap" << std::endl; vtkm::cont::ArrayHandleBasic array; vtkm::cont::ArrayCopy(vtkm::cont::make_ArrayHandleConstant(0, ARRAY_SIZE), array); array.Allocate(ARRAY_SIZE); this->Invoke( AddFunctor{}, vtkm::cont::ArrayHandleIndex(EXTENDED_SIZE), ArrayToRawPointer(array)); auto portal = array.ReadPortal(); T expectedValue = T(OVERLAP); for (vtkm::Id arrayIndex = 0; arrayIndex < ARRAY_SIZE; ++arrayIndex) { T foundValue = portal.Get(arrayIndex); VTKM_TEST_ASSERT(test_equal(foundValue, expectedValue), foundValue, " != ", expectedValue); } } VTKM_CONT void TestAll() { TestLoad(); TestStore(); TestAdd(); TestAnd(); TestOr(); TestXor(); TestNot(); TestCompareAndSwap(); } }; struct TestFunctor { template VTKM_CONT void operator()(T) const { AtomicTests().TestAll(); } }; void Run() { VTKM_TEST_ASSERT(vtkm::ListHas::value); vtkm::testing::Testing::TryTypes(TestFunctor{}, vtkm::AtomicTypesSupported{}); } } // anonymous namespace int UnitTestAtomic(int argc, char* argv[]) { return vtkm::cont::testing::Testing::Run(Run, argc, argv); }