2016-12-16 22:05:45 +00:00
|
|
|
//============================================================================
|
|
|
|
// Copyright (c) Kitware, Inc.
|
|
|
|
// All rights reserved.
|
|
|
|
// See LICENSE.txt for details.
|
2019-04-15 23:24:21 +00:00
|
|
|
//
|
2016-12-16 22:05:45 +00:00
|
|
|
// 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.
|
|
|
|
//============================================================================
|
2019-03-08 01:32:56 +00:00
|
|
|
#ifndef vtk_m_cont_ArrayHandle_hxx
|
|
|
|
#define vtk_m_cont_ArrayHandle_hxx
|
|
|
|
|
|
|
|
#include <vtkm/cont/ArrayHandle.h>
|
2016-12-16 22:05:45 +00:00
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
namespace vtkm
|
|
|
|
{
|
|
|
|
namespace cont
|
|
|
|
{
|
2016-12-16 22:05:45 +00:00
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>::InternalStruct::InternalStruct(
|
|
|
|
const typename ArrayHandle<T, S>::StorageType& storage)
|
|
|
|
: ControlArray(storage)
|
2020-03-16 17:19:23 +00:00
|
|
|
, ControlArrayValid(new bool(true))
|
2019-11-13 17:33:35 +00:00
|
|
|
, ExecutionArrayValid(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>::InternalStruct::InternalStruct(typename ArrayHandle<T, S>::StorageType&& storage)
|
|
|
|
: ControlArray(std::move(storage))
|
2020-03-16 17:19:23 +00:00
|
|
|
, ControlArrayValid(new bool(true))
|
2019-11-13 17:33:35 +00:00
|
|
|
, ExecutionArrayValid(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>::ArrayHandle()
|
2019-11-13 17:33:35 +00:00
|
|
|
: Internals(std::make_shared<InternalStruct>())
|
2017-02-13 19:31:20 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>::ArrayHandle(const ArrayHandle<T, S>& src)
|
2017-02-13 19:31:20 +00:00
|
|
|
: Internals(src.Internals)
|
2017-05-18 14:29:41 +00:00
|
|
|
{
|
|
|
|
}
|
2017-02-13 19:31:20 +00:00
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2018-10-23 20:21:05 +00:00
|
|
|
ArrayHandle<T, S>::ArrayHandle(ArrayHandle<T, S>&& src) noexcept
|
2017-02-13 19:31:20 +00:00
|
|
|
: Internals(std::move(src.Internals))
|
2017-05-18 14:29:41 +00:00
|
|
|
{
|
|
|
|
}
|
2017-02-13 19:31:20 +00:00
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>::ArrayHandle(const typename ArrayHandle<T, S>::StorageType& storage)
|
2019-11-13 17:33:35 +00:00
|
|
|
: Internals(std::make_shared<InternalStruct>(storage))
|
2017-02-13 19:31:20 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2018-04-04 15:08:51 +00:00
|
|
|
template <typename T, typename S>
|
2018-10-23 20:21:05 +00:00
|
|
|
ArrayHandle<T, S>::ArrayHandle(typename ArrayHandle<T, S>::StorageType&& storage) noexcept
|
2019-11-13 17:33:35 +00:00
|
|
|
: Internals(std::make_shared<InternalStruct>(std::move(storage)))
|
2018-04-04 15:08:51 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>::~ArrayHandle()
|
2017-02-13 19:31:20 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
ArrayHandle<T, S>& ArrayHandle<T, S>::operator=(const ArrayHandle<T, S>& src)
|
2017-02-13 19:31:20 +00:00
|
|
|
{
|
|
|
|
this->Internals = src.Internals;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2018-10-23 20:21:05 +00:00
|
|
|
ArrayHandle<T, S>& ArrayHandle<T, S>::operator=(ArrayHandle<T, S>&& src) noexcept
|
2017-02-13 19:31:20 +00:00
|
|
|
{
|
|
|
|
this->Internals = std::move(src.Internals);
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
typename ArrayHandle<T, S>::StorageType& ArrayHandle<T, S>::GetStorage()
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
// A Token should not be declared within the scope of a lock. when the token goes out of scope
|
|
|
|
// it will attempt to aquire the lock, which is undefined behavior of the thread already has
|
|
|
|
// the lock.
|
|
|
|
vtkm::cont::Token token;
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
LockType lock = this->GetLock();
|
|
|
|
|
|
|
|
this->SyncControlArray(lock, token);
|
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
|
|
|
{
|
|
|
|
return *this->Internals->GetControlArray(lock);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw vtkm::cont::ErrorInternal(
|
|
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
const typename ArrayHandle<T, S>::StorageType& ArrayHandle<T, S>::GetStorage() const
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
// A Token should not be declared within the scope of a lock. when the token goes out of scope
|
|
|
|
// it will attempt to aquire the lock, which is undefined behavior of the thread already has
|
|
|
|
// the lock.
|
|
|
|
vtkm::cont::Token token;
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
LockType lock = this->GetLock();
|
|
|
|
|
|
|
|
this->SyncControlArray(lock, token);
|
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
|
|
|
{
|
|
|
|
return *this->Internals->GetControlArray(lock);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw vtkm::cont::ErrorInternal(
|
|
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2020-01-24 01:01:30 +00:00
|
|
|
typename ArrayHandle<T, S>::StorageType::PortalType ArrayHandle<T, S>::GetPortalControl()
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
// A Token should not be declared within the scope of a lock. when the token goes out of scope
|
|
|
|
// it will attempt to aquire the lock, which is undefined behavior of the thread already has
|
|
|
|
// the lock.
|
|
|
|
vtkm::cont::Token token;
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
LockType lock = this->GetLock();
|
|
|
|
|
|
|
|
this->SyncControlArray(lock, token);
|
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
|
|
|
{
|
|
|
|
// If the user writes into the iterator we return, then the execution
|
|
|
|
// array will become invalid. Play it safe and release the execution
|
|
|
|
// resources. (Use the const version to preserve the execution array.)
|
|
|
|
this->ReleaseResourcesExecutionInternal(lock, token);
|
|
|
|
return this->Internals->GetControlArray(lock)->GetPortal();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw vtkm::cont::ErrorInternal(
|
|
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2020-01-24 01:01:30 +00:00
|
|
|
typename ArrayHandle<T, S>::StorageType::PortalConstType ArrayHandle<T, S>::GetPortalConstControl()
|
|
|
|
const
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
// A Token should not be declared within the scope of a lock. when the token goes out of scope
|
|
|
|
// it will attempt to aquire the lock, which is undefined behavior of the thread already has
|
|
|
|
// the lock.
|
|
|
|
vtkm::cont::Token token;
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
LockType lock = this->GetLock();
|
|
|
|
|
|
|
|
this->SyncControlArray(lock, token);
|
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
|
|
|
{
|
|
|
|
return this->Internals->GetControlArray(lock)->GetPortalConst();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw vtkm::cont::ErrorInternal(
|
|
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-24 01:01:30 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
typename ArrayHandle<T, S>::ReadPortalType ArrayHandle<T, S>::ReadPortal() const
|
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
// A Token should not be declared within the scope of a lock. when the token goes out of scope
|
|
|
|
// it will attempt to aquire the lock, which is undefined behavior of the thread already has
|
|
|
|
// the lock.
|
|
|
|
vtkm::cont::Token token;
|
2020-03-11 20:48:37 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
LockType lock = this->GetLock();
|
2020-03-11 20:48:37 +00:00
|
|
|
this->WaitToRead(lock, token);
|
2020-01-24 01:01:30 +00:00
|
|
|
|
2020-06-08 18:25:07 +00:00
|
|
|
this->SyncControlArray(lock, token);
|
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
|
|
|
{
|
2020-10-07 17:01:51 +00:00
|
|
|
return ReadPortalType(this->Internals->GetControlArray(lock)->GetPortalConst());
|
2020-06-08 18:25:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw vtkm::cont::ErrorInternal(
|
|
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
|
|
}
|
2020-01-24 01:01:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
typename ArrayHandle<T, S>::WritePortalType ArrayHandle<T, S>::WritePortal() const
|
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
// A Token should not be declared within the scope of a lock. when the token goes out of scope
|
|
|
|
// it will attempt to aquire the lock, which is undefined behavior of the thread already has
|
|
|
|
// the lock.
|
|
|
|
vtkm::cont::Token token;
|
2020-03-11 20:48:37 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
LockType lock = this->GetLock();
|
2020-03-11 20:48:37 +00:00
|
|
|
this->WaitToWrite(lock, token);
|
2020-01-24 01:01:30 +00:00
|
|
|
|
2020-06-08 18:25:07 +00:00
|
|
|
this->SyncControlArray(lock, token);
|
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
|
|
|
{
|
|
|
|
// If the user writes into the iterator we return, then the execution
|
|
|
|
// array will become invalid. Play it safe and release the execution
|
|
|
|
// resources. (Use the const version to preserve the execution array.)
|
|
|
|
this->ReleaseResourcesExecutionInternal(lock, token);
|
2020-10-07 17:01:51 +00:00
|
|
|
return WritePortalType(this->Internals->GetControlArray(lock)->GetPortal());
|
2020-06-08 18:25:07 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
throw vtkm::cont::ErrorInternal(
|
|
|
|
"ArrayHandle::SyncControlArray did not make control array valid.");
|
|
|
|
}
|
2020-01-24 01:01:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2019-11-13 17:33:35 +00:00
|
|
|
vtkm::Id ArrayHandle<T, S>::GetNumberOfValues(LockType& lock) const
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
return this->Internals->GetControlArray(lock)->GetNumberOfValues();
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
2019-11-13 17:33:35 +00:00
|
|
|
else if (this->Internals->IsExecutionArrayValid(lock))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
return this->Internals->GetExecutionArray(lock)->GetNumberOfValues();
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2020-11-30 22:47:13 +00:00
|
|
|
void ArrayHandle<T, S>::Shrink(vtkm::Id numberOfValues, vtkm::cont::Token& token)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2017-03-08 23:49:03 +00:00
|
|
|
VTKM_ASSERT(numberOfValues >= 0);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
2017-03-08 23:49:03 +00:00
|
|
|
if (numberOfValues > 0)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
LockType lock = this->GetLock();
|
|
|
|
|
|
|
|
vtkm::Id originalNumberOfValues = this->GetNumberOfValues(lock);
|
2017-03-08 23:49:03 +00:00
|
|
|
|
|
|
|
if (numberOfValues < originalNumberOfValues)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-06-08 18:25:07 +00:00
|
|
|
this->WaitToWrite(lock, token);
|
2019-11-13 17:33:35 +00:00
|
|
|
if (this->Internals->IsControlArrayValid(lock))
|
2017-03-08 23:49:03 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->GetControlArray(lock)->Shrink(numberOfValues);
|
2017-03-08 23:49:03 +00:00
|
|
|
}
|
2019-11-13 17:33:35 +00:00
|
|
|
if (this->Internals->IsExecutionArrayValid(lock))
|
2017-03-08 23:49:03 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->GetExecutionArray(lock)->Shrink(numberOfValues);
|
2017-03-08 23:49:03 +00:00
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
2017-03-08 23:49:03 +00:00
|
|
|
else if (numberOfValues == originalNumberOfValues)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2017-03-08 23:49:03 +00:00
|
|
|
// Nothing to do.
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
2017-03-08 23:49:03 +00:00
|
|
|
else // numberOfValues > originalNumberOfValues
|
|
|
|
{
|
2017-05-18 14:29:41 +00:00
|
|
|
throw vtkm::cont::ErrorBadValue("ArrayHandle::Shrink cannot be used to grow array.");
|
2017-03-08 23:49:03 +00:00
|
|
|
}
|
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
VTKM_ASSERT(this->GetNumberOfValues(lock) == numberOfValues);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
2017-03-08 23:49:03 +00:00
|
|
|
else // numberOfValues == 0
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2017-03-08 23:49:03 +00:00
|
|
|
// If we are shrinking to 0, there is nothing to save and we might as well
|
|
|
|
// free up memory. Plus, some storage classes expect that data will be
|
|
|
|
// deallocated when the size goes to zero.
|
2020-11-30 22:47:13 +00:00
|
|
|
this->Allocate(0, token);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
template <typename DeviceAdapterTag>
|
|
|
|
typename ArrayHandle<T, S>::template ExecutionTypes<DeviceAdapterTag>::PortalConst
|
2020-01-16 22:39:10 +00:00
|
|
|
ArrayHandle<T, S>::PrepareForInput(DeviceAdapterTag device, vtkm::cont::Token& token) const
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
|
|
|
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
|
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
LockType lock = this->GetLock();
|
2020-01-19 22:50:48 +00:00
|
|
|
this->WaitToRead(lock, token);
|
2019-11-13 17:33:35 +00:00
|
|
|
|
|
|
|
if (!this->Internals->IsControlArrayValid(lock) && !this->Internals->IsExecutionArrayValid(lock))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2017-02-14 22:27:34 +00:00
|
|
|
// Want to use an empty array.
|
|
|
|
// Set up ArrayHandle state so this actually works.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->GetControlArray(lock)->Allocate(0);
|
|
|
|
this->Internals->SetControlArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 18:25:07 +00:00
|
|
|
this->PrepareForDevice(lock, token, device);
|
2019-11-13 17:33:35 +00:00
|
|
|
auto portal = this->Internals->GetExecutionArray(lock)->PrepareForInput(
|
2020-01-19 06:36:14 +00:00
|
|
|
!this->Internals->IsExecutionArrayValid(lock), device, token);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->SetExecutionArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
|
|
|
return portal;
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
template <typename DeviceAdapterTag>
|
|
|
|
typename ArrayHandle<T, S>::template ExecutionTypes<DeviceAdapterTag>::Portal
|
2020-01-16 22:39:10 +00:00
|
|
|
ArrayHandle<T, S>::PrepareForOutput(vtkm::Id numberOfValues,
|
|
|
|
DeviceAdapterTag device,
|
|
|
|
vtkm::cont::Token& token)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
|
|
|
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
|
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
LockType lock = this->GetLock();
|
2020-01-19 22:50:48 +00:00
|
|
|
this->WaitToWrite(lock, token);
|
2019-11-13 17:33:35 +00:00
|
|
|
|
2016-12-16 22:05:45 +00:00
|
|
|
// Invalidate any control arrays.
|
|
|
|
// Should the control array resource be released? Probably not a good
|
|
|
|
// idea when shared with execution.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->SetControlArrayValid(lock, false);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
2020-06-08 18:25:07 +00:00
|
|
|
this->PrepareForDevice(lock, token, device);
|
2020-01-19 06:36:14 +00:00
|
|
|
auto portal =
|
|
|
|
this->Internals->GetExecutionArray(lock)->PrepareForOutput(numberOfValues, device, token);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
|
|
|
// We are assuming that the calling code will fill the array using the
|
|
|
|
// iterators we are returning, so go ahead and mark the execution array as
|
|
|
|
// having valid data. (A previous version of this class had a separate call
|
|
|
|
// to mark the array as filled, but that was onerous to call at the the
|
|
|
|
// right time and rather pointless since it is basically always the case
|
|
|
|
// that the array is going to be filled before anything else. In this
|
|
|
|
// implementation the only access to the array is through the iterators
|
|
|
|
// returned from this method, so you would have to work to invalidate this
|
|
|
|
// assumption anyway.)
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->SetExecutionArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
|
|
|
return portal;
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
template <typename DeviceAdapterTag>
|
|
|
|
typename ArrayHandle<T, S>::template ExecutionTypes<DeviceAdapterTag>::Portal
|
2020-01-16 22:39:10 +00:00
|
|
|
ArrayHandle<T, S>::PrepareForInPlace(DeviceAdapterTag device, vtkm::cont::Token& token)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
|
|
|
VTKM_IS_DEVICE_ADAPTER_TAG(DeviceAdapterTag);
|
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
LockType lock = this->GetLock();
|
2020-01-19 22:50:48 +00:00
|
|
|
this->WaitToWrite(lock, token);
|
2019-11-13 17:33:35 +00:00
|
|
|
|
|
|
|
if (!this->Internals->IsControlArrayValid(lock) && !this->Internals->IsExecutionArrayValid(lock))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2017-02-14 22:27:34 +00:00
|
|
|
// Want to use an empty array.
|
|
|
|
// Set up ArrayHandle state so this actually works.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->GetControlArray(lock)->Allocate(0);
|
|
|
|
this->Internals->SetControlArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2020-06-08 18:25:07 +00:00
|
|
|
this->PrepareForDevice(lock, token, device);
|
2019-11-13 17:33:35 +00:00
|
|
|
auto portal = this->Internals->GetExecutionArray(lock)->PrepareForInPlace(
|
2020-01-19 06:36:14 +00:00
|
|
|
!this->Internals->IsExecutionArrayValid(lock), device, token);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->SetExecutionArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
|
|
|
// Invalidate any control arrays since their data will become invalid when
|
|
|
|
// the execution data is overwritten. Don't actually release the control
|
|
|
|
// array. It may be shared as the execution array.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->SetControlArrayValid(lock, false);
|
2016-12-16 22:05:45 +00:00
|
|
|
|
|
|
|
return portal;
|
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
|
|
|
template <typename DeviceAdapterTag>
|
2020-06-08 18:25:07 +00:00
|
|
|
void ArrayHandle<T, S>::PrepareForDevice(LockType& lock,
|
|
|
|
vtkm::cont::Token& token,
|
|
|
|
DeviceAdapterTag device) const
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
if (this->Internals->GetExecutionArray(lock) != nullptr)
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
if (this->Internals->GetExecutionArray(lock)->IsDeviceAdapter(DeviceAdapterTag()))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
|
|
|
// Already have manager for correct device adapter. Nothing to do.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Have the wrong manager. Delete the old one and create a new one
|
2020-01-16 22:39:10 +00:00
|
|
|
// of the right type. (TODO: it would be possible for the array handle
|
|
|
|
// to hold references to execution arrays on multiple devices. When data
|
|
|
|
// are written on one devices, all the other devices should get cleared.)
|
|
|
|
|
|
|
|
// BUG: There is a non-zero chance that while waiting for the write lock, another thread
|
|
|
|
// could change the ExecutionInterface, which would cause problems. In the future we should
|
|
|
|
// support multiple devices, in which case we would not have to delete one execution array
|
|
|
|
// to load another.
|
2020-06-09 00:15:13 +00:00
|
|
|
// BUG: The current implementation does not allow the ArrayHandle to be on two devices
|
|
|
|
// at the same time. Thus, it is not possible for two simultaneously read from the same
|
|
|
|
// ArrayHandle on two different devices. This might cause unexpected deadlocks.
|
|
|
|
this->WaitToWrite(lock, token, true); // Make sure no one is reading device array
|
2020-06-08 18:25:07 +00:00
|
|
|
this->SyncControlArray(lock, token);
|
2016-12-16 22:05:45 +00:00
|
|
|
// Need to change some state that does not change the logical state from
|
|
|
|
// an external point of view.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->DeleteExecutionArray(lock);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
2017-05-18 14:29:41 +00:00
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
|
|
|
|
// Need to change some state that does not change the logical state from
|
|
|
|
// an external point of view.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->NewExecutionArray(lock, device);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
|
2017-05-18 14:29:41 +00:00
|
|
|
template <typename T, typename S>
|
2020-06-08 18:25:07 +00:00
|
|
|
void ArrayHandle<T, S>::SyncControlArray(LockType& lock, vtkm::cont::Token& token) const
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
if (!this->Internals->IsControlArrayValid(lock))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2020-01-19 22:50:48 +00:00
|
|
|
// It may be the case that `SyncControlArray` is called from a method that has a `Token`.
|
|
|
|
// However, if we are here, that `Token` should not already be attached to this array.
|
|
|
|
// If it were, then there should be no reason to move data arround (unless the `Token`
|
|
|
|
// was used when preparing for multiple devices, which it should not be used like that).
|
2020-06-08 18:25:07 +00:00
|
|
|
this->WaitToRead(lock, token);
|
2020-01-16 22:39:10 +00:00
|
|
|
|
2016-12-16 22:05:45 +00:00
|
|
|
// Need to change some state that does not change the logical state from
|
|
|
|
// an external point of view.
|
2019-11-13 17:33:35 +00:00
|
|
|
if (this->Internals->IsExecutionArrayValid(lock))
|
2016-12-16 22:05:45 +00:00
|
|
|
{
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->GetExecutionArray(lock)->RetrieveOutputData(
|
|
|
|
this->Internals->GetControlArray(lock));
|
|
|
|
this->Internals->SetControlArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// This array is in the null state (there is nothing allocated), but
|
|
|
|
// the calling function wants to do something with the array. Put this
|
|
|
|
// class into a valid state by allocating an array of size 0.
|
2019-11-13 17:33:35 +00:00
|
|
|
this->Internals->GetControlArray(lock)->Allocate(0);
|
|
|
|
this->Internals->SetControlArrayValid(lock, true);
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-08 18:25:07 +00:00
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
bool ArrayHandle<T, S>::CanRead(const LockType& lock, const vtkm::cont::Token& token) const
|
|
|
|
{
|
|
|
|
// If the token is already attached to this array, then we allow reading.
|
|
|
|
if (token.IsAttached(this->Internals->GetWriteCount(lock)) ||
|
|
|
|
token.IsAttached(this->Internals->GetReadCount(lock)))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is anyone else waiting at the top of the queue, we cannot access this array.
|
|
|
|
auto& queue = this->Internals->GetQueue(lock);
|
|
|
|
if (!queue.empty() && (queue.front() != token))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No one else is waiting, so we can read the array as long as no one else is writing.
|
|
|
|
return (*this->Internals->GetWriteCount(lock) < 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
bool ArrayHandle<T, S>::CanWrite(const LockType& lock, const vtkm::cont::Token& token) const
|
|
|
|
{
|
|
|
|
// If the token is already attached to this array, then we allow writing.
|
|
|
|
if (token.IsAttached(this->Internals->GetWriteCount(lock)) ||
|
|
|
|
token.IsAttached(this->Internals->GetReadCount(lock)))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If there is anyone else waiting at the top of the queue, we cannot access this array.
|
|
|
|
auto& queue = this->Internals->GetQueue(lock);
|
|
|
|
if (!queue.empty() && (queue.front() != token))
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// No one else is waiting, so we can write the array as long as no one else is reading or writing.
|
|
|
|
return ((*this->Internals->GetWriteCount(lock) < 1) &&
|
|
|
|
(*this->Internals->GetReadCount(lock) < 1));
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
void ArrayHandle<T, S>::WaitToRead(LockType& lock, vtkm::cont::Token& token) const
|
|
|
|
{
|
|
|
|
this->Enqueue(lock, token);
|
|
|
|
|
|
|
|
// Note that if you deadlocked here, that means that you are trying to do a read operation on an
|
|
|
|
// array where an object is writing to it.
|
|
|
|
this->Internals->ConditionVariable.wait(
|
|
|
|
lock, [&lock, &token, this] { return this->CanRead(lock, token); });
|
|
|
|
|
|
|
|
token.Attach(this->Internals,
|
|
|
|
this->Internals->GetReadCount(lock),
|
|
|
|
lock,
|
|
|
|
&this->Internals->ConditionVariable);
|
|
|
|
|
|
|
|
// We successfully attached the token. Pop it off the queue.
|
|
|
|
auto& queue = this->Internals->GetQueue(lock);
|
|
|
|
if (!queue.empty() && queue.front() == token)
|
|
|
|
{
|
|
|
|
queue.pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
2020-06-09 00:15:13 +00:00
|
|
|
void ArrayHandle<T, S>::WaitToWrite(LockType& lock, vtkm::cont::Token& token, bool fakeRead) const
|
2020-06-08 18:25:07 +00:00
|
|
|
{
|
|
|
|
this->Enqueue(lock, token);
|
|
|
|
|
|
|
|
// Note that if you deadlocked here, that means that you are trying to do a write operation on an
|
|
|
|
// array where an object is reading or writing to it.
|
|
|
|
this->Internals->ConditionVariable.wait(
|
|
|
|
lock, [&lock, &token, this] { return this->CanWrite(lock, token); });
|
|
|
|
|
2020-06-09 00:15:13 +00:00
|
|
|
if (!fakeRead)
|
|
|
|
{
|
|
|
|
token.Attach(this->Internals,
|
|
|
|
this->Internals->GetWriteCount(lock),
|
|
|
|
lock,
|
|
|
|
&this->Internals->ConditionVariable);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// A current feature limitation of ArrayHandle is that it can only exist on one device at
|
|
|
|
// a time. Thus, if a read request comes in for a different device, the prepare has to
|
|
|
|
// get satisfy a write lock to boot the array off the existing device. However, we don't
|
|
|
|
// want to attach the Token as a write lock because the resulting state is for reading only
|
|
|
|
// and others might also want to read. So, we have to pretend that this is a read lock even
|
|
|
|
// though we have to make a change to the array.
|
|
|
|
//
|
|
|
|
// The main point is, this condition is a hack that should go away once ArrayHandle supports
|
|
|
|
// multiple devices at once.
|
|
|
|
token.Attach(this->Internals,
|
|
|
|
this->Internals->GetReadCount(lock),
|
|
|
|
lock,
|
|
|
|
&this->Internals->ConditionVariable);
|
|
|
|
}
|
2020-06-08 18:25:07 +00:00
|
|
|
|
|
|
|
// We successfully attached the token. Pop it off the queue.
|
|
|
|
auto& queue = this->Internals->GetQueue(lock);
|
|
|
|
if (!queue.empty() && queue.front() == token)
|
|
|
|
{
|
|
|
|
queue.pop_front();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
void ArrayHandle<T, S>::Enqueue(const vtkm::cont::Token& token) const
|
|
|
|
{
|
|
|
|
LockType lock = this->GetLock();
|
|
|
|
this->Enqueue(lock, token);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename T, typename S>
|
|
|
|
void ArrayHandle<T, S>::Enqueue(const LockType& lock, const vtkm::cont::Token& token) const
|
|
|
|
{
|
|
|
|
if (token.IsAttached(this->Internals->GetWriteCount(lock)) ||
|
|
|
|
token.IsAttached(this->Internals->GetReadCount(lock)))
|
|
|
|
{
|
|
|
|
// Do not need to enqueue if we are already attached.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto& queue = this->Internals->GetQueue(lock);
|
|
|
|
if (std::find(queue.begin(), queue.end(), token.GetReference()) != queue.end())
|
|
|
|
{
|
|
|
|
// This token is already in the queue.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this->Internals->GetQueue(lock).push_back(token.GetReference());
|
|
|
|
}
|
2016-12-16 22:05:45 +00:00
|
|
|
}
|
2018-06-08 15:56:40 +00:00
|
|
|
} // vtkm::cont
|
|
|
|
|
2019-03-08 01:32:56 +00:00
|
|
|
#endif //vtk_m_cont_ArrayHandle_hxx
|