Files
rocm-systems/shared/amdgpu-windows-interop/pal/inc/util/palDeque.h
T
Scott Todd fa772be675 Reapply amdgpu-windows-interop revert. (#1893)
## Overview and rationale

This reverts https://github.com/ROCm/rocm-systems/pull/1886, which...
* Re-applies https://github.com/ROCm/rocm-systems/pull/1866
* Reverts https://github.com/ROCm/rocm-systems/pull/1728

(So it restores the [`amdgpu-windows-interop/`](https://github.com/ROCm/rocm-systems/tree/develop/shared/amdgpu-windows-interop) folder back to the state from a few weeks ago)

The rationale for this change is at https://github.com/ROCm/rocm-systems/pull/1866:
> Last PAL update broke applications on gfx12 Windows.

## Cross-repository change details

That PR failed to build but was merged with this explanation:

> TheRock CI Windows build fails as expected with this revert.
> 
> References to these PAL members need to be stripped out in a patch on TheRock.
> 
> ```
> 11.3	C:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\device\pal\palubercapturemgr.cpp(152): error C2039: 'RegisterTraceStateChangeCallback': is not a member of 'GpuUtil::TraceSession'
> 11.4	C:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\gpuUtil\palTraceSession.h(372): note: see declaration of 'GpuUtil::TraceSession'
> 11.4	C:\home\runner\_work\rocm-systems\rocm-systems\projects\clr\rocclr\device\pal\palubercapturemgr.cpp(195): error C2039: 'UnregisterTraceStateChangeCallback': is not a member of 'GpuUtil::TraceSession'
> 11.4	C:\home\runner\_work\rocm-systems\rocm-systems\shared\amdgpu-windows-interop\pal\inc\gpuUtil\palTraceSession.h(372): note: see declaration of 'GpuUtil::TraceSession'
> ```

The patch in TheRock was updated in https://github.com/ROCm/TheRock/pull/2154. This rolls forward by updating the ref for TheRock.

That original PR could have been sequenced differently to avoid a build break - perhaps by
* Pointing to a branch in TheRock with the patch rebased
* Deleting the patch in the workflows here but holding a local copy of the path to be applied in workflows
* Landing the patch as a normal commit instead of carrying it at all

## Test plan

1. Watch TheRock CI here (https://github.com/ROCm/rocm-systems/actions/runs/19447202693/job/55644411119?pr=1893)
2. Build locally:
    
    ```bash
    # In rocm-systems
    git am --whitespace=nowarn D:\projects\TheRock\patches\amd-mainline\rocm-systems\0001-Revert-SWDEV-543498-Some-compute-Ubertrace-profiles-.patch
    git am --whitespace=nowarn D:\projects\TheRock\patches\amd-mainline\rocm-systems\0003-Use-is_versioned-true-consistently-in-both-Comgr-Loa.patch
    git am --whitespace=nowarn D:\projects\TheRock\patches\amd-mainline\rocm-systems\0006-Explicitly-load-libamdhip64.so.7.patch
    # Note: the build fails with the observed errors if patch 0001 is not applied!
    
    # In TheRock
    cmake -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_C_COMPILER=cl.exe -DCMAKE_CXX_COMPILER=cl.exe \
      -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
      -DPython3_EXECUTABLE=d:/projects/TheRock/.venv/Scripts/python \
      -DTHEROCK_ROCM_SYSTEMS_SOURCE_DIR=d:/projects/TheRock/../rocm-systems \  # IMPORTANT
      -DTHEROCK_AMDGPU_FAMILIES=gfx110X-all \
      -DBUILD_TESTING=ON \
      -DTHEROCK_ENABLE_ALL=ON \
      -Damd-llvm_BUILD_TYPE=RelWithDebInfo \
      -S D:/projects/TheRock \
      -B D:/projects/TheRock/build \
      -G Ninja
    
    cmake --build D:/projects/TheRock/build --target hip-clr
    # [build] Build finished with exit code 0
    cmake --build D:/projects/TheRock/build --target ocl-clr+dist
    # [build] Build finished with exit code 0
    ```
2025-11-18 07:17:06 -08:00

320 rivejä
13 KiB
C++

/*
***********************************************************************************************************************
*
* Copyright (c) 2014-2025 Advanced Micro Devices, Inc. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
**********************************************************************************************************************/
/**
***********************************************************************************************************************
* @file palDeque.h
* @brief PAL utility collection Deque and DequeIterator class declarations.
***********************************************************************************************************************
*/
#pragma once
#include "palAssert.h"
#include "palSysMemory.h"
namespace Util
{
// Forward declarations.
template<typename T, typename Allocator> class Deque;
/// @internal Private structure used by Deque and its iterators to store chunks of data elements.
struct DequeBlockHeader
{
DequeBlockHeader* pPrev; ///< Pointer to the previous block.
DequeBlockHeader* pNext; ///< Pointer to the next block.
void* pStart; ///< Pointer to the first element in this block.
void* pEnd; ///< Pointer to the last element in this block.
};
/**
***********************************************************************************************************************
* @brief Iterator for traversal of elements in a Deque collection.
*
* Allows traversal of all elements in a Deque going either forwards or backwards. If you traverse off either end of
* the deque, then you must create a new iterator by calling either the Deque's Begin() or End() method.
***********************************************************************************************************************
*/
template<typename T, typename Allocator>
class DequeIterator
{
public:
/// Trivial destructor.
~DequeIterator() { }
/// Returns a pointer to the current element. Will return null if we've gone past the end.
T* Get() const { return m_pCurrent; }
/// Advances the iterator to the next position (move forward).
void Next();
/// Advances the iterator to the previous position (move backward).
void Prev();
/// Check if the element the iterator references is valid.
bool IsValid() const { return m_pCurrent != nullptr; }
private:
DequeIterator(const Deque<T, Allocator>* pDeque, DequeBlockHeader* pHeader, T* pCurrent);
const Deque<T, Allocator>*const m_pDeque; // The Deque we're iterating over.
const DequeBlockHeader* m_pCurrentHeader; // The block we're iterating over.
T* m_pCurrent; // Pointer to the current element. Null if we've gone past the
// end.
PAL_DISALLOW_DEFAULT_CTOR(DequeIterator);
// Although this is a transgression of coding standards, it means that Deque does not need to have a public
// interface specifically to implement this class. The added encapsulation this provides is worthwhile.
friend class Deque<T, Allocator>;
};
/**
***********************************************************************************************************************
* @brief Simple templated deque container - a double-ended queue.
*
* This is meant for storing elements of an arbitrary (but uniform) type. Operations which this class supports are:
*
* - Insertion from the front and back.
* - Deletion from the front and back.
* - Forwards and reverse iteration
*
* @warning This class is not thread-safe for push, pop, or iteration!
*
* @note This class is only designed to work with native types and POD-style structures. If it is needed to have a Deque
* of complex objects with nontrivial destructors, copy constructors or assign operators, then a specialized
* implementation of CleanupElement() will need to be explicitly defined.
***********************************************************************************************************************
*/
template<typename T, typename Allocator>
class Deque
{
public:
/// Constructor.
///
/// @param [in] pAllocator The allocator that will allocate memory if required.
Deque(Allocator*const pAllocator, size_t numElementsPerBlock = 256);
~Deque();
/// Returns the number of elements in the deque.
size_t NumElements() const { return m_numElements; }
/// Returns an iterator pointing to the first element in the deque.
///
/// @returns An iterator pointing at the front end of the deque.
DequeIterator<T, Allocator> Begin() const { return DequeIterator<T, Allocator>(this, m_pFrontHeader, m_pFront); }
/// Returns an iterator pointing to the last element in the deque.
///
/// This is somewhat different from std::deque.End() which returns a pointer to the theoretical object _past_ the
/// end of the deque.
///
/// @returns An iterator pointing at the back end of the deque.
DequeIterator<T, Allocator> End() const { return DequeIterator<T, Allocator>(this, m_pBackHeader, m_pBack); }
///@{
/// Returns the element at the location specified.
///
/// @warning Calling this function with an out-of-bounds index will cause an access violation!
///
/// @param [in] index Integer location of the element needed.
///
/// @returns The element at location specified by index by reference
T& At(uint32 index);
const T& At(uint32 index) const;
T& operator[](uint32 index);
const T& operator[](uint32 index) const;
///@}
/// Returns the object at the front of the deque.
///
/// @warning This will cause an access violation if called on an empty deque!
///
/// @returns Reference to the item stored at the front end of the deque.
T& Front() const
{
PAL_ASSERT(m_numElements != 0);
return *m_pFront;
}
/// Returns the object at the tail of the deque.
///
/// @warning This will cause an access violation if called on an empty deque!
///
/// @returns Reference to the item stored at the back end of the deque.
T& Back() const
{
PAL_ASSERT(m_numElements != 0);
return *m_pBack;
}
/// Pushes a copy of the specified item onto the front of the deque.
///
/// @param [in] data Item to be added to the front of the deque.
///
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
/// failed because of an internal failure to allocate system memory.
Result PushFront(const T& data);
/// Emplaces a newly constructed item onto the front of the deque.
///
/// @param [in] args arguments used to construct the new item.
///
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
/// failed because of an internal failure to allocate system memory.
template<typename... Args>
Result EmplaceFront(Args&&... args);
/// Pushes a copy of the specified item onto the back of the deque.
///
/// @param [in] data Item to be added to the back of the deque.
///
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
/// failed because of an internal failure to allocate system memory.
Result PushBack(const T& data);
/// Emplaces a newly constructed item onto the back of the deque.
///
/// @param [in] args arguments used to construct the new item.
///
/// @returns @ref Success if the item was successfully added to the deque or @ref ErrorOutOfMemory if the operation
/// failed because of an internal failure to allocate system memory.
template<typename... Args>
Result EmplaceBack(Args&&... args);
/// Pops the first item off the front of the deque, returning the popped value.
///
/// @param [out] pOut Item popped off the front of the deque.
///
/// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque
/// is empty.
Result PopFront(T* pOut);
/// Pops the first item off the back of the deque, returning the popped value.
///
/// @param [out] pOut Item popped off the back of the deque.
///
/// @returns @ref Success if the item was successfully popped from the deque or @ref ErrorUnavailable if the deque
/// is empty.
Result PopBack(T* pOut);
private:
Result AllocateFront(T**);
Result AllocateBack(T**);
DequeBlockHeader* AllocateNewBlock();
void FreeUnusedBlock(DequeBlockHeader* pHeader);
// A helper function to avoid duplication in const and non-const versions of At().
T& InternalAt(uint32 index) const;
size_t m_numElements; // Number of elements
const size_t m_numElementsPerBlock; // Block granularity when we need to alloc a new one
DequeBlockHeader* m_pFrontHeader; // First block of data elements, null for empty deques.
DequeBlockHeader* m_pBackHeader; // Last block of data elements, null for empty deques/
T* m_pFront; // First data element, null for empty deques.
T* m_pBack; // Last data element, null for empty deques.
DequeBlockHeader* m_pLazyFreeHeader; // Cached pointer to the most-recently freed block.
Allocator*const m_pAllocator; // Pointer to the allocator for this deque.
PAL_DISALLOW_COPY_AND_ASSIGN(Deque);
// Although this is a transgression of coding standards, it prevents DequeIterator requiring a public constructor;
// constructing a 'bare' DequeIterator (i.e. without calling Deque::GetIterator) can never be a legal operation, so
// this means that these two classes are much safer to use.
friend class DequeIterator<T, Allocator>;
};
// =====================================================================================================================
template<typename T, typename Allocator>
Deque<T, Allocator>::Deque(
Allocator*const pAllocator,
size_t numElementsPerBlock)
:
m_numElements(0),
m_numElementsPerBlock(numElementsPerBlock),
m_pFrontHeader(nullptr),
m_pBackHeader(nullptr),
m_pFront(nullptr),
m_pBack(nullptr),
m_pLazyFreeHeader(nullptr),
m_pAllocator(pAllocator)
{
}
// =====================================================================================================================
// Frees all of the blocks this object allocated over its lifetime.
template<typename T, typename Allocator>
Deque<T, Allocator>::~Deque()
{
if (!std::is_trivial<T>::value)
{
while (m_pFrontHeader != nullptr)
{
// Explicitly destroy the removed value since it's non-trivial and advance.
// We must destroy all of them in the current block before freeing it.
m_pFront->~T();
++m_pFront;
--m_numElements;
if ((m_pFront == m_pFrontHeader->pEnd) || (m_numElements == 0))
{
// Okay, the front block is now empty. Free it and advance to the next block.
DequeBlockHeader* pBlockToFree = m_pFrontHeader;
m_pFrontHeader = m_pFrontHeader->pNext;
PAL_SAFE_FREE(pBlockToFree, m_pAllocator);
if (m_pFrontHeader != nullptr)
{
// Fixup to the new block.
m_pFront = static_cast<T*>(m_pFrontHeader->pStart);
}
}
}
}
else
{
// Elements are trivial so skip iterating through elements and free each block.
while (m_pFrontHeader != nullptr)
{
DequeBlockHeader* pBlockToFree = m_pFrontHeader;
m_pFrontHeader = m_pFrontHeader->pNext;
PAL_SAFE_FREE(pBlockToFree, m_pAllocator);
}
}
if (m_pLazyFreeHeader != nullptr)
{
PAL_SAFE_FREE(m_pLazyFreeHeader, m_pAllocator);
}
}
} // Util