mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-10-05 18:08:59 +00:00
185 lines
5.2 KiB
C++
185 lines
5.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/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, 2, 3);
|
|
CHECK_OBJECT(object2, 2, 3);
|
|
CHECK_OBJECT(object3, 2, 3);
|
|
|
|
std::cout << " Detach from inner token (through scoping)" << std::endl;
|
|
}
|
|
CHECK_OBJECT(object1, 1, 2);
|
|
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 500 milliseconds for thread to lock" << std::endl;
|
|
// 500 milliseconds 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::milliseconds(500));
|
|
|
|
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);
|
|
}
|