vtk-m2/vtkm/cont/testing/UnitTestToken.cxx
Kenneth Moreland 5f44f3727c Add ability to attach token to standard ArrayHandle
Added new versions of PrepareFor* methods that take a token in addition
to the other arguments. The ArrayHandle attaches itself to the token and
will not allow operations that make the returned portal invalid until
the token goes out of scope.

Later the old versions will be deprecated.
2020-02-25 07:41:36 -07:00

185 lines
5.5 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/Token.h>
#include <vtkm/cont/testing/Testing.h>
#include <chrono>
#include <future>
#include <thread>
namespace
{
struct TestObject
{
vtkm::cont::Token::ReferenceCount* TokenCount;
std::mutex* Mutex;
std::condition_variable* ConditionVariable;
vtkm::Id* ReferenceCount;
TestObject()
: TokenCount(new vtkm::cont::Token::ReferenceCount(0))
, Mutex(new std::mutex)
, ConditionVariable(new std::condition_variable)
, ReferenceCount(new vtkm::Id(1))
{
}
TestObject(const TestObject& src)
: TokenCount(src.TokenCount)
, Mutex(src.Mutex)
, ConditionVariable(src.ConditionVariable)
, ReferenceCount(src.ReferenceCount)
{
std::unique_lock<std::mutex> lock(*this->Mutex);
(*this->ReferenceCount)++;
}
~TestObject()
{
std::unique_lock<std::mutex> lock(*this->Mutex);
(*this->ReferenceCount)--;
if (*this->ReferenceCount == 0)
{
lock.unlock();
delete this->TokenCount;
delete this->Mutex;
delete this->ConditionVariable;
delete this->ReferenceCount;
}
}
void Attach(vtkm::cont::Token& token)
{
token.Attach(*this, this->TokenCount, this->Mutex, this->ConditionVariable);
}
};
#define CHECK_OBJECT(object, expectedTokens, expectedRefs) \
VTKM_TEST_ASSERT(*(object).TokenCount == (expectedTokens), \
"Expected object to have token count of ", \
(expectedTokens), \
". It actually was ", \
*(object).TokenCount); \
VTKM_TEST_ASSERT(*(object).ReferenceCount == (expectedRefs), \
"Expected object to have reference count of ", \
(expectedRefs), \
". It actually was ", \
*(object).ReferenceCount)
void TestBasicAttachDetatch()
{
std::cout << "Test basic attach detach." << std::endl;
std::cout << " Create objects" << std::endl;
TestObject object1;
TestObject object2;
TestObject object3;
CHECK_OBJECT(object1, 0, 1);
CHECK_OBJECT(object2, 0, 1);
CHECK_OBJECT(object3, 0, 1);
std::cout << " Create outer token" << std::endl;
vtkm::cont::Token outerToken;
std::cout << " Attach outer token" << std::endl;
object1.Attach(outerToken);
object2.Attach(outerToken);
object3.Attach(outerToken);
CHECK_OBJECT(object1, 1, 2);
CHECK_OBJECT(object2, 1, 2);
CHECK_OBJECT(object3, 1, 2);
{
std::cout << " Create/Attach inner token" << std::endl;
vtkm::cont::Token innerToken;
object1.Attach(innerToken);
object2.Attach(innerToken);
object3.Attach(innerToken);
CHECK_OBJECT(object1, 2, 3);
CHECK_OBJECT(object2, 2, 3);
CHECK_OBJECT(object3, 2, 3);
std::cout << " Recursively attach outer token" << std::endl;
object1.Attach(outerToken);
CHECK_OBJECT(object1, 3, 4);
CHECK_OBJECT(object2, 2, 3);
CHECK_OBJECT(object3, 2, 3);
std::cout << " Detach from inner token (through scoping)" << std::endl;
}
CHECK_OBJECT(object1, 2, 3);
CHECK_OBJECT(object2, 1, 2);
CHECK_OBJECT(object3, 1, 2);
std::cout << " Detatch outer token" << std::endl;
outerToken.DetachFromAll();
CHECK_OBJECT(object1, 0, 1);
CHECK_OBJECT(object2, 0, 1);
CHECK_OBJECT(object3, 0, 1);
}
void WaitForDetachment(TestObject object)
{
std::unique_lock<std::mutex> lock(*object.Mutex);
object.ConditionVariable->wait(lock, [&object] { return *object.TokenCount < 1; });
std::cout << " Thread woke up" << std::endl;
}
void TestThreadWake()
{
std::cout << "Testing thread wakeup" << std::endl;
TestObject object;
CHECK_OBJECT(object, 0, 1);
vtkm::cont::Token token;
object.Attach(token);
CHECK_OBJECT(object, 1, 2);
std::cout << " Launching coordinated thread" << std::endl;
auto future = std::async(std::launch::async, WaitForDetachment, object);
std::cout << " Sleep 2 seconds for thread to lock" << std::endl;
// 2 seconds should be ample time for the spawned thread to launch. If the systems busy then
// we might actually unlock the object before the thread gets there, but hopefully on some
// systems it will test correctly.
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << " Main thread woke up. Detach token." << std::endl;
token.DetachFromAll();
std::cout << " Wait for thread to finish. Could deadlock if did not properly wake." << std::endl;
future.get();
std::cout << " Returned to main thread" << std::endl;
CHECK_OBJECT(object, 0, 1);
}
void DoTest()
{
TestBasicAttachDetatch();
TestThreadWake();
}
} // anonymous namespace
int UnitTestToken(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(DoTest, argc, argv);
}