Merge branch 'development' into cmake/SPEC-7484
Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com> # Conflicts: # Code/Editor/ToolBox.cpp
This commit is contained in:
@@ -84,7 +84,7 @@ namespace AZ
|
||||
else
|
||||
{
|
||||
AZ::Debug::Trace::Instance().Assert(__FILE__, __LINE__, AZ_FUNCTION_SIGNATURE,
|
||||
"Bus has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads");
|
||||
"Bus %s has multiple threads in its callstack records. Configure MutexType on the bus, or don't send to it from multiple threads", BusType::GetName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -268,7 +268,7 @@ namespace AZ
|
||||
m_messages.pop();
|
||||
if (numMessages == 1)
|
||||
{
|
||||
m_messages.get_container().clear(); // If it was the last message, free all memory.
|
||||
m_messages = {};
|
||||
}
|
||||
}
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -280,7 +280,7 @@ namespace AZ
|
||||
void Clear()
|
||||
{
|
||||
AZStd::lock_guard<MutexType> lock(m_messagesMutex);
|
||||
m_messages.get_container().clear();
|
||||
m_messages = {};
|
||||
}
|
||||
|
||||
void SetActive(bool isActive)
|
||||
@@ -289,7 +289,7 @@ namespace AZ
|
||||
m_isActive = isActive;
|
||||
if (!m_isActive)
|
||||
{
|
||||
m_messages.get_container().clear();
|
||||
m_messages = {};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Jobs/Job.h>
|
||||
#include <AzCore/Jobs/JobCancelGroup.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
|
||||
namespace AZ
|
||||
{
|
||||
Job::Job(bool isAutoDelete, AZ::JobContext* context, bool isCompletion, AZ::s8 priority)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
m_context = context;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context = JobContext::GetParentContext();
|
||||
}
|
||||
|
||||
unsigned int countAndFlags = 1;
|
||||
if (isAutoDelete)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_AUTO_DELETE;
|
||||
}
|
||||
if (isCompletion)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_COMPLETION;
|
||||
}
|
||||
countAndFlags |= (unsigned int)((priority << FLAG_PRIORITY_START_BIT) & FLAG_PRIORITY_MASK);
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
StoreDependent(NULL);
|
||||
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SETUP);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
|
||||
void Job::Start()
|
||||
{
|
||||
//jobs are created with a count set to 1, we remove that count to allow the job to start
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Jobs must be in the setup state before they can be started"));
|
||||
SetState(STATE_STARTED);
|
||||
#endif
|
||||
DecrementDependentCount();
|
||||
}
|
||||
|
||||
void Job::Reset(bool isClearDependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_PROCESSING), "Jobs must not be running when they are reset");
|
||||
SetState(STATE_SETUP);
|
||||
#endif
|
||||
unsigned int countAndFlags = GetDependentCountAndFlags();
|
||||
AZ_Assert((countAndFlags & (unsigned int)FLAG_AUTO_DELETE) == 0, "You can't call reset on AutoDelete jobs!");
|
||||
// Remove the FLAG_DEPENDENTCOUNT_MASK and FLAG_CHILD_JOBS flags
|
||||
countAndFlags = (countAndFlags & (~(FLAG_DEPENDENTCOUNT_MASK) & ~(FLAG_CHILD_JOBS))) | 1;
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
if (isClearDependent)
|
||||
{
|
||||
StoreDependent(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in setup state before it can be re-initialized"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Job::SetDependent(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in the setup state"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
void Job::SetDependentStarted(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
//We don't require the dependent to be in STATE_SETUP, the user can call this from a context where they
|
||||
//know the dependent has not started yet, although it is in STATE_STARTED already, e.g. if SetDependent
|
||||
//is called from a job which the dependent is already dependent on.
|
||||
//Note that if the user gets this wrong, the dependent may start before this job is finished, and the asserts
|
||||
//may not even trigger due to race conditions. Hence why this function is 'experts only'.
|
||||
AZ_Assert((dependent->m_state == STATE_SETUP) || (dependent->m_state == STATE_STARTED)
|
||||
|| (dependent->m_state == STATE_SUSPENDED), "Dependent must be in the setup, started, or suspended state");
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
void Job::SetDependentChild(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_PROCESSING, "Dependent must be processing to add a child");
|
||||
#endif
|
||||
dependent->IncrementDependentCountAndSetChildFlag();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
void Job::SetContinuation(Job* continuationJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Continuation jobs can only be set while we are processing, otherwise a regular dependent should be used");
|
||||
#endif
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent) //nothing to do if there is no dependent... doesn't usually happen, except with synchronous processing and assists
|
||||
{
|
||||
continuationJob->SetDependentStarted(dependent);
|
||||
}
|
||||
}
|
||||
|
||||
void Job::StartAsChild(Job* childJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Child jobs can only be added while we are processing");
|
||||
#endif
|
||||
childJob->SetDependentChild(this);
|
||||
childJob->Start();
|
||||
}
|
||||
|
||||
void Job::WaitForChildren()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "We must be currently processing in order to suspend");
|
||||
#endif
|
||||
if (GetDependentCount() != 0)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SUSPENDED);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
m_context->GetJobManager().SuspendJobUntilReady(this);
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_PROCESSING);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
AZ_Assert(GetDependentCount() == 0, "Suspended job has resumed, but still has non-zero dependent count, bug in JobManager?");
|
||||
}
|
||||
|
||||
bool Job::IsCancelled() const
|
||||
{
|
||||
JobCancelGroup* cancelGroup = m_context->GetCancelGroup();
|
||||
if (cancelGroup && cancelGroup->IsCancelled())
|
||||
{
|
||||
if (!IsCompletion()) // always run completion jobs, as they can be holding a synchronization primitive
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Job::IsAutoDelete() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_AUTO_DELETE) ? true : false;
|
||||
}
|
||||
|
||||
bool Job::IsCompletion() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_COMPLETION) ? true : false;
|
||||
}
|
||||
|
||||
void Job::StartAndAssistUntilComplete()
|
||||
{
|
||||
m_context->GetJobManager().StartJobAndAssistUntilComplete(this);
|
||||
}
|
||||
|
||||
void Job::StartAndWaitForCompletion()
|
||||
{
|
||||
//check if we are in a worker thread or a general user thread
|
||||
Job* currentJob = m_context->GetJobManager().GetCurrentJob();
|
||||
if (currentJob)
|
||||
{
|
||||
//worker thread, so just suspend this current job until the empty job completes
|
||||
currentJob->StartAsChild(this);
|
||||
currentJob->WaitForChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartAndAssistUntilComplete();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int Job::GetDependentCount() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & FLAG_DEPENDENTCOUNT_MASK);
|
||||
}
|
||||
|
||||
void Job::IncrementDependentCount()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
++m_dependentCountAndFlags;
|
||||
#else
|
||||
m_dependentCountAndFlags.fetch_add(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Job::IncrementDependentCountAndSetChildFlag()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
int oldCount = m_dependentCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
m_dependentCountAndFlags = (m_dependentCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
#else
|
||||
//use a single atomic operation to increment the count and set the child flag if possible
|
||||
unsigned int oldCountAndFlags, newCountAndFlags;
|
||||
do
|
||||
{
|
||||
oldCountAndFlags = m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
int oldCount = oldCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
newCountAndFlags = (oldCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
} while (!m_dependentCountAndFlags.compare_exchange_weak(oldCountAndFlags, newCountAndFlags, AZStd::memory_order_acq_rel, AZStd::memory_order_acquire));
|
||||
#endif
|
||||
}
|
||||
|
||||
void Job::DecrementDependentCount()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_STARTED)
|
||||
|| (m_state == STATE_PROCESSING) || (m_state == STATE_SUSPENDED), //child jobs
|
||||
"Job dependent count should not be decremented after job is already pending");
|
||||
#endif
|
||||
AZ_Assert(GetDependentCount() > 0, ("Job dependent count is already zero"));
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags--;
|
||||
#else
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags.fetch_sub(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
unsigned int count = countAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
if (count == 1)
|
||||
{
|
||||
if (!(countAndFlags & FLAG_CHILD_JOBS))
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_STARTED, "Job has not been started but the dependent count is zero, must be a dependency error");
|
||||
SetState(STATE_PENDING);
|
||||
#endif
|
||||
m_context->GetJobManager().AddPendingJob(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ::s8 Job::GetPriority() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() >> FLAG_PRIORITY_START_BIT) & 0xff;
|
||||
}
|
||||
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent = job;
|
||||
}
|
||||
|
||||
Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent;
|
||||
}
|
||||
|
||||
void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags = countAndFlags;
|
||||
}
|
||||
|
||||
unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags;
|
||||
}
|
||||
#else
|
||||
void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent.store(job, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
|
||||
void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags.store(countAndFlags, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -5,15 +5,14 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZCORE_JOBS_JOB_H
|
||||
#define AZCORE_JOBS_JOB_H 1
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Jobs/JobCancelGroup.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/base.h>
|
||||
#include <AzCore/Jobs/JobCancelGroup.h>
|
||||
#include <AzCore/Jobs/JobContext.h>
|
||||
#include <AzCore/Jobs/JobManager.h>
|
||||
#include <AzCore/std/parallel/atomic.h>
|
||||
|
||||
#include <AzCore/Memory/PoolAllocator.h>
|
||||
|
||||
#if defined(_DEBUG)
|
||||
@@ -234,319 +233,22 @@ namespace AZ
|
||||
//would require atomic ops to set/read it, so not really worth it.
|
||||
int m_state;
|
||||
};
|
||||
|
||||
//============================================================================================================
|
||||
//============================================================================================================
|
||||
//============================================================================================================
|
||||
|
||||
inline Job::Job(bool isAutoDelete, JobContext* context, bool isCompletion, AZ::s8 priority)
|
||||
{
|
||||
if (context)
|
||||
{
|
||||
m_context = context;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_context = JobContext::GetParentContext();
|
||||
}
|
||||
|
||||
unsigned int countAndFlags = 1;
|
||||
if (isAutoDelete)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_AUTO_DELETE;
|
||||
}
|
||||
if (isCompletion)
|
||||
{
|
||||
countAndFlags |= (unsigned int)FLAG_COMPLETION;
|
||||
}
|
||||
countAndFlags |= (unsigned int)((priority << FLAG_PRIORITY_START_BIT) & FLAG_PRIORITY_MASK);
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
StoreDependent(NULL);
|
||||
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SETUP);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::Start()
|
||||
{
|
||||
//jobs are created with a count set to 1, we remove that count to allow the job to start
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Jobs must be in the setup state before they can be started"));
|
||||
SetState(STATE_STARTED);
|
||||
#endif
|
||||
DecrementDependentCount();
|
||||
}
|
||||
|
||||
inline void Job::Reset(bool isClearDependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_PROCESSING), "Jobs must not be running when they are reset");
|
||||
SetState(STATE_SETUP);
|
||||
#endif
|
||||
unsigned int countAndFlags = GetDependentCountAndFlags();
|
||||
AZ_Assert((countAndFlags & (unsigned int)FLAG_AUTO_DELETE) == 0, "You can't call reset on AutoDelete jobs!");
|
||||
// Remove the FLAG_DEPENDENTCOUNT_MASK and FLAG_CHILD_JOBS flags
|
||||
countAndFlags = (countAndFlags & (~(FLAG_DEPENDENTCOUNT_MASK) & ~(FLAG_CHILD_JOBS))) | 1;
|
||||
SetDependentCountAndFlags(countAndFlags);
|
||||
if (isClearDependent)
|
||||
{
|
||||
StoreDependent(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in setup state before it can be re-initialized"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependent(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_SETUP, ("Dependent must be in the setup state"));
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentStarted(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
//We don't require the dependent to be in STATE_SETUP, the user can call this from a context where they
|
||||
//know the dependent has not started yet, although it is in STATE_STARTED already, e.g. if SetDependent
|
||||
//is called from a job which the dependent is already dependent on.
|
||||
//Note that if the user gets this wrong, the dependent may start before this job is finished, and the asserts
|
||||
//may not even trigger due to race conditions. Hence why this function is 'experts only'.
|
||||
AZ_Assert((dependent->m_state == STATE_SETUP) || (dependent->m_state == STATE_STARTED)
|
||||
|| (dependent->m_state == STATE_SUSPENDED), "Dependent must be in the setup, started, or suspended state");
|
||||
#endif
|
||||
dependent->IncrementDependentCount();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentChild(Job* dependent)
|
||||
{
|
||||
AZ_Assert(!GetDependent(), ("Job already has a dependent, should be cleared after the job is done"));
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_SETUP, ("Dependent can only be set before the jobs are started"));
|
||||
AZ_Assert(dependent->m_state == STATE_PROCESSING, "Dependent must be processing to add a child");
|
||||
#endif
|
||||
dependent->IncrementDependentCountAndSetChildFlag();
|
||||
StoreDependent(dependent);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetContinuation(Job* continuationJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Continuation jobs can only be set while we are processing, otherwise a regular dependent should be used");
|
||||
#endif
|
||||
Job* dependent = GetDependent();
|
||||
if (dependent) //nothing to do if there is no dependent... doesn't usually happen, except with synchronous processing and assists
|
||||
{
|
||||
continuationJob->SetDependentStarted(dependent);
|
||||
}
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::StartAsChild(Job* childJob)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "Child jobs can only be added while we are processing");
|
||||
#endif
|
||||
childJob->SetDependentChild(this);
|
||||
childJob->Start();
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::WaitForChildren()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_PROCESSING, "We must be currently processing in order to suspend");
|
||||
#endif
|
||||
if (GetDependentCount() != 0)
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_SUSPENDED);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
m_context->GetJobManager().SuspendJobUntilReady(this);
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
SetState(STATE_PROCESSING);
|
||||
#endif // AZ_DEBUG_JOB_STATE
|
||||
}
|
||||
AZ_Assert(GetDependentCount() == 0, "Suspended job has resumed, but still has non-zero dependent count, bug in JobManager?");
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool Job::IsCancelled() const
|
||||
{
|
||||
JobCancelGroup* cancelGroup = m_context->GetCancelGroup();
|
||||
if (cancelGroup && cancelGroup->IsCancelled())
|
||||
{
|
||||
if (!IsCompletion()) // always run completion jobs, as they can be holding a synchronization primitive
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool Job::IsAutoDelete() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_AUTO_DELETE) ? true : false;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool Job::IsCompletion() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & (unsigned int)FLAG_COMPLETION) ? true : false;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::StartAndAssistUntilComplete()
|
||||
{
|
||||
m_context->GetJobManager().StartJobAndAssistUntilComplete(this);
|
||||
}
|
||||
|
||||
inline void Job::StartAndWaitForCompletion()
|
||||
{
|
||||
//check if we are in a worker thread or a general user thread
|
||||
Job* currentJob = m_context->GetJobManager().GetCurrentJob();
|
||||
if (currentJob)
|
||||
{
|
||||
//worker thread, so just suspend this current job until the empty job completes
|
||||
currentJob->StartAsChild(this);
|
||||
currentJob->WaitForChildren();
|
||||
}
|
||||
else
|
||||
{
|
||||
StartAndAssistUntilComplete();
|
||||
}
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE JobContext* Job::GetContext() const
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Inline implementations
|
||||
inline JobContext* Job::GetContext() const
|
||||
{
|
||||
return m_context;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE unsigned int Job::GetDependentCount() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() & FLAG_DEPENDENTCOUNT_MASK);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::IncrementDependentCount()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
++m_dependentCountAndFlags;
|
||||
#else
|
||||
m_dependentCountAndFlags.fetch_add(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Job::IncrementDependentCountAndSetChildFlag()
|
||||
{
|
||||
AZ_Assert(GetDependentCount() < FLAG_DEPENDENTCOUNT_MASK, "Dependent count overflow");
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
int oldCount = m_dependentCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
m_dependentCountAndFlags = (m_dependentCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
#else
|
||||
//use a single atomic operation to increment the count and set the child flag if possible
|
||||
unsigned int oldCountAndFlags, newCountAndFlags;
|
||||
do
|
||||
{
|
||||
oldCountAndFlags = m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
int oldCount = oldCountAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
newCountAndFlags = (oldCountAndFlags & ~FLAG_DEPENDENTCOUNT_MASK) | (oldCount + 1) | FLAG_CHILD_JOBS;
|
||||
} while (!m_dependentCountAndFlags.compare_exchange_weak(oldCountAndFlags, newCountAndFlags, AZStd::memory_order_acq_rel, AZStd::memory_order_acquire));
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void Job::DecrementDependentCount()
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert((m_state == STATE_SETUP) || (m_state == STATE_STARTED)
|
||||
|| (m_state == STATE_PROCESSING) || (m_state == STATE_SUSPENDED), //child jobs
|
||||
"Job dependent count should not be decremented after job is already pending");
|
||||
#endif
|
||||
AZ_Assert(GetDependentCount() > 0, ("Job dependent count is already zero"));
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags--;
|
||||
#else
|
||||
unsigned int countAndFlags = m_dependentCountAndFlags.fetch_sub(1, AZStd::memory_order_acq_rel);
|
||||
#endif
|
||||
unsigned int count = countAndFlags & FLAG_DEPENDENTCOUNT_MASK;
|
||||
if (count == 1)
|
||||
{
|
||||
if (!(countAndFlags & FLAG_CHILD_JOBS))
|
||||
{
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_Assert(m_state == STATE_STARTED, "Job has not been started but the dependent count is zero, must be a dependency error");
|
||||
SetState(STATE_PENDING);
|
||||
#endif
|
||||
m_context->GetJobManager().AddPendingJob(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline AZ::s8 Job::GetPriority() const
|
||||
{
|
||||
return (GetDependentCountAndFlags() >> FLAG_PRIORITY_START_BIT) & 0xff;
|
||||
}
|
||||
|
||||
#ifdef AZ_DEBUG_JOB_STATE
|
||||
AZ_FORCE_INLINE void Job::SetState(int state)
|
||||
inline void Job::SetState(int state)
|
||||
{
|
||||
m_state = state;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef AZCORE_JOBS_IMPL_SYNCHRONOUS
|
||||
AZ_FORCE_INLINE void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent = job;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags = countAndFlags;
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags;
|
||||
}
|
||||
#else
|
||||
AZ_FORCE_INLINE void Job::StoreDependent(Job* job)
|
||||
{
|
||||
m_dependent.store(job, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE Job* Job::GetDependent() const
|
||||
{
|
||||
return m_dependent.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void Job::SetDependentCountAndFlags(unsigned int countAndFlags)
|
||||
{
|
||||
m_dependentCountAndFlags.store(countAndFlags, AZStd::memory_order_release);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE unsigned int Job::GetDependentCountAndFlags() const
|
||||
{
|
||||
return m_dependentCountAndFlags.load(AZStd::memory_order_acquire);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -42,8 +42,6 @@ namespace AZStd
|
||||
class unordered_multiset;
|
||||
template<AZStd::size_t NumBits>
|
||||
class bitset;
|
||||
template<class T, class Container/* = AZStd::deque<T>*/ >
|
||||
class stack;
|
||||
|
||||
template<class T>
|
||||
class intrusive_ptr;
|
||||
|
||||
@@ -236,6 +236,17 @@ namespace AZ
|
||||
*/
|
||||
ClassBuilder* ClassElement(Crc32 elementIdCrc, const char* description);
|
||||
|
||||
|
||||
/**
|
||||
* Declare element with attributes that belong to the class SerializeContext::Class, this is a logical structure, you can have one or more GroupElementToggles.
|
||||
* T must be a boolean variable that will enable and disable each DataElement attached to this structure.
|
||||
* \param description - Descriptive name of the field that will typically appear in a tooltip.
|
||||
* \param memberVariable - reference to the member variable so we can bind to serialization data.
|
||||
*/
|
||||
template<class T>
|
||||
ClassBuilder* GroupElementToggle(const char* description, T memberVariable);
|
||||
|
||||
|
||||
/**
|
||||
* Declare element with an associated UI handler that does not represent a specific class member variable.
|
||||
* \param uiId - name of a UI handler used to display the element
|
||||
@@ -515,6 +526,15 @@ namespace AZ
|
||||
return this;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// ClassElement
|
||||
//=========================================================================
|
||||
template<class T>
|
||||
inline EditContext::ClassBuilder* EditContext::ClassBuilder::GroupElementToggle(const char* name, T memberVariable)
|
||||
{
|
||||
return DataElement(AZ::Edit::ClassElements::Group, memberVariable, name, name, "");
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// UIElement
|
||||
//=========================================================================
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace AZ::Internal
|
||||
// and avoid all this logic.
|
||||
|
||||
using namespace AZ::SettingsRegistryMergeUtils;
|
||||
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
|
||||
|
||||
AZ::IO::FixedMaxPath engineRoot;
|
||||
if (auto engineManifestPath = AZ::Utils::GetEngineManifestPath(); !engineManifestPath.empty())
|
||||
@@ -72,45 +73,16 @@ namespace AZ::Internal
|
||||
struct EngineInfo
|
||||
{
|
||||
AZ::IO::FixedMaxPath m_path;
|
||||
AZ::SettingsRegistryInterface::FixedValueString m_moniker;
|
||||
FixedValueString m_moniker;
|
||||
};
|
||||
|
||||
struct EnginePathsVisitor : public AZ::SettingsRegistryInterface::Visitor
|
||||
{
|
||||
void Visit(
|
||||
[[maybe_unused]] AZStd::string_view path, [[maybe_unused]] AZStd::string_view valueName,
|
||||
[[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
|
||||
[[maybe_unused]] AZ::SettingsRegistryInterface::Type type, AZStd::string_view value) override
|
||||
{
|
||||
m_enginePaths.emplace_back(EngineInfo{AZ::IO::FixedMaxPath{value}.LexicallyNormal(), {}});
|
||||
}
|
||||
|
||||
AZ::SettingsRegistryInterface::VisitResponse Traverse(
|
||||
[[maybe_unused]] AZStd::string_view path, AZStd::string_view valueName,
|
||||
AZ::SettingsRegistryInterface::VisitAction action, AZ::SettingsRegistryInterface::Type type) override
|
||||
{
|
||||
auto response = AZ::SettingsRegistryInterface::VisitResponse::Continue;
|
||||
if (action == AZ::SettingsRegistryInterface::VisitAction::Begin)
|
||||
{
|
||||
if (type == AZ::SettingsRegistryInterface::Type::Array)
|
||||
{
|
||||
if (valueName.compare("engines") != 0)
|
||||
{
|
||||
response = AZ::SettingsRegistryInterface::VisitResponse::Skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (action == AZ::SettingsRegistryInterface::VisitAction::Value)
|
||||
{
|
||||
if (type == AZ::SettingsRegistryInterface::Type::String)
|
||||
{
|
||||
if (valueName.compare("path") != 0)
|
||||
{
|
||||
response = AZ::SettingsRegistryInterface::VisitResponse::Skip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
m_enginePaths.emplace_back(EngineInfo{ AZ::IO::FixedMaxPath{value}.LexicallyNormal(), FixedValueString{valueName} });
|
||||
}
|
||||
|
||||
AZStd::vector<EngineInfo> m_enginePaths{};
|
||||
@@ -119,11 +91,11 @@ namespace AZ::Internal
|
||||
EnginePathsVisitor pathVisitor;
|
||||
if (manifestLoaded)
|
||||
{
|
||||
auto enginePathsKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engines", EngineManifestRootKey);
|
||||
auto enginePathsKey = FixedValueString::format("%s/engines_path", EngineManifestRootKey);
|
||||
settingsRegistry.Visit(pathVisitor, enginePathsKey);
|
||||
}
|
||||
|
||||
const auto engineMonikerKey = AZ::SettingsRegistryInterface::FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
|
||||
const auto engineMonikerKey = FixedValueString::format("%s/engine_name", EngineSettingsRootKey);
|
||||
|
||||
AZStd::set<AZ::IO::FixedMaxPath> projectPathsNotFound;
|
||||
|
||||
@@ -135,7 +107,15 @@ namespace AZ::Internal
|
||||
if (settingsRegistry.MergeSettingsFile(
|
||||
engineSettingsPath.Native(), AZ::SettingsRegistryInterface::Format::JsonMergePatch, EngineSettingsRootKey))
|
||||
{
|
||||
settingsRegistry.Get(engineInfo.m_moniker, engineMonikerKey);
|
||||
FixedValueString engineName;
|
||||
settingsRegistry.Get(engineName, engineMonikerKey);
|
||||
AZ_Warning("SettingsRegistryMergeUtils",engineInfo.m_moniker == engineName,
|
||||
R"(The engine name key "%s" mapped to engine path "%s" within the global manifest of "%s")"
|
||||
R"( does not match the "engine_name" field "%s" in the engine.json)" "\n"
|
||||
"This engine should be re-registered.",
|
||||
engineInfo.m_moniker.c_str(), engineInfo.m_path.c_str(), engineManifestPath.c_str(),
|
||||
engineName.c_str())
|
||||
engineInfo.m_moniker = engineName;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -221,6 +221,7 @@ set(FILES
|
||||
Jobs/Internal/JobManagerWorkStealing.cpp
|
||||
Jobs/Internal/JobManagerWorkStealing.h
|
||||
Jobs/Internal/JobNotify.h
|
||||
Jobs/Job.cpp
|
||||
Jobs/Job.h
|
||||
Jobs/JobCancelGroup.h
|
||||
Jobs/JobCompletion.h
|
||||
|
||||
@@ -5,206 +5,17 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZSTD_QUEUE_H
|
||||
#define AZSTD_QUEUE_H 1
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/functional_basic.h>
|
||||
#include <queue>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
/**
|
||||
* FIFO queue complaint with \ref CStd (23.2.3.1)
|
||||
* The only extension we have is that we allow access
|
||||
* to the underlying container via: get_container function.
|
||||
* Check the queue \ref AZStdExamples.
|
||||
*/
|
||||
template<class T, class Container = AZStd::deque<T> >
|
||||
class queue
|
||||
{
|
||||
enum
|
||||
{
|
||||
CONTAINER_VERSION = 1
|
||||
};
|
||||
public:
|
||||
typedef queue<T, Container> this_type;
|
||||
typedef Container container_type;
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::reference reference;
|
||||
typedef typename Container::const_reference const_reference;
|
||||
|
||||
AZ_FORCE_INLINE queue() {}
|
||||
|
||||
AZ_FORCE_INLINE explicit queue(const container_type& container)
|
||||
: m_container(container) {}
|
||||
AZ_FORCE_INLINE bool empty() const { return m_container.empty(); }
|
||||
AZ_FORCE_INLINE size_type size() const { return m_container.size(); }
|
||||
AZ_FORCE_INLINE reference front() { return m_container.front(); }
|
||||
AZ_FORCE_INLINE const_reference front() const { return m_container.front(); }
|
||||
AZ_FORCE_INLINE reference back() { return m_container.back(); }
|
||||
AZ_FORCE_INLINE const_reference back() const { return m_container.back(); }
|
||||
AZ_FORCE_INLINE void push(const value_type& value) { m_container.push_back(value); }
|
||||
AZ_FORCE_INLINE void pop() { m_container.pop_front(); }
|
||||
|
||||
AZ_FORCE_INLINE void push() { m_container.push_back(); }
|
||||
|
||||
AZ_FORCE_INLINE queue(this_type&& rhs)
|
||||
: m_container(AZStd::move(rhs.m_container)) {}
|
||||
AZ_FORCE_INLINE explicit queue(Container&& container)
|
||||
: m_container(AZStd::move(container)) {}
|
||||
this_type& operator=(this_type&& rhs)
|
||||
{
|
||||
m_container = AZStd::move(rhs.m_container);
|
||||
return (*this);
|
||||
}
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); }
|
||||
template<class... Args>
|
||||
void emplace(Args&&... args) { m_container.emplace_back(AZStd::forward<Args>(args)...); }
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
protected:
|
||||
Container m_container;
|
||||
};
|
||||
|
||||
// queue TEMPLATE FUNCTIONS
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator==(const AZStd::queue<T, Container>& left, const AZStd::queue<T, Container>& right)
|
||||
{
|
||||
return left.get_container() == right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator!=(const AZStd::queue<T, Container>& left, const AZStd::queue<T, Container>& right)
|
||||
{
|
||||
return left.get_container() != right.get_container();
|
||||
}
|
||||
|
||||
/* template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator<(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() < right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() > right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE operator<=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() <= right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() >= right.get_container();
|
||||
}*/
|
||||
|
||||
/**
|
||||
* Priority queue is complaint with \ref CStd (23.2.3.2)
|
||||
* The only extension we have is that we allow access
|
||||
* to the underlying container via: get_container function.
|
||||
* Check the priority_queue \ref AZStdExamples.
|
||||
*/
|
||||
template<class T, class Container = AZStd::vector<T>, class Predicate = AZStd::less<typename Container::value_type> >
|
||||
class priority_queue
|
||||
{
|
||||
enum
|
||||
{
|
||||
CONTAINER_VERSION = 1
|
||||
};
|
||||
public:
|
||||
typedef priority_queue<T, Container, Predicate> this_type;
|
||||
typedef Container container_type;
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::reference reference;
|
||||
typedef typename Container::const_reference const_reference;
|
||||
|
||||
AZ_FORCE_INLINE priority_queue() {}
|
||||
AZ_FORCE_INLINE explicit priority_queue(const Predicate& comp)
|
||||
: m_comp(comp) {}
|
||||
AZ_FORCE_INLINE priority_queue(const Predicate& comp, const container_type& container)
|
||||
: m_container(container)
|
||||
, m_comp(comp)
|
||||
{
|
||||
// construct by copying specified container, comparator
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), comp);
|
||||
}
|
||||
template<class InputIterator>
|
||||
AZ_FORCE_INLINE priority_queue(InputIterator first, InputIterator last)
|
||||
: m_container(first, last)
|
||||
, m_comp()
|
||||
{
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
AZ_FORCE_INLINE priority_queue(InputIterator first, InputIterator last, const Predicate& comp)
|
||||
: m_container(first, last)
|
||||
, m_comp(comp)
|
||||
{ // construct by copying [_First, _Last), specified comparator
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
template<class InputIterator>
|
||||
AZ_FORCE_INLINE priority_queue(InputIterator first, InputIterator last, const Predicate& comp, const container_type& container)
|
||||
: m_container(container)
|
||||
, m_comp(comp)
|
||||
{ // construct by copying [_First, _Last), container, and comparator
|
||||
m_container.insert(m_container.end(), first, last);
|
||||
AZStd::make_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE bool empty() const { return m_container.empty(); }
|
||||
AZ_FORCE_INLINE size_type size() const { return m_container.size(); }
|
||||
AZ_FORCE_INLINE const_reference top() const { return m_container.front(); }
|
||||
AZ_FORCE_INLINE reference top() { return m_container.front(); }
|
||||
AZ_FORCE_INLINE void push(const value_type& value)
|
||||
{
|
||||
m_container.push_back(value);
|
||||
AZStd::push_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE void pop()
|
||||
{
|
||||
AZStd::pop_heap(m_container.begin(), m_container.end(), m_comp);
|
||||
m_container.pop_back();
|
||||
}
|
||||
|
||||
AZ_FORCE_INLINE priority_queue(this_type&& rhs)
|
||||
: m_container(AZStd::move(rhs.m_container))
|
||||
, m_comp(AZStd::move(rhs.m_comp)) {}
|
||||
AZ_FORCE_INLINE explicit priority_queue(const Predicate& pred, Container&& container)
|
||||
: m_container(AZStd::move(container))
|
||||
, m_comp(pred) {}
|
||||
this_type& operator=(this_type&& rhs)
|
||||
{
|
||||
m_container = AZStd::move(rhs.m_container);
|
||||
m_comp = AZStd::move(rhs.m_comp);
|
||||
return (*this);
|
||||
}
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); AZStd::push_heap(m_container.begin(), m_container.end(), m_comp); }
|
||||
template<class Args>
|
||||
void emplace(Args&& args) { m_container.emplace_back(AZStd::forward<Args>(args)); AZStd::push_heap(m_container.begin(), m_container.end(), m_comp); }
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); AZStd::swap(m_comp, rhs.m_comp); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
protected:
|
||||
Container m_container;
|
||||
Predicate m_comp;
|
||||
};
|
||||
template<class T, class Container = AZStd::deque<T>>
|
||||
using queue = std::queue<T, Container>;
|
||||
template<class T, class Container = AZStd::vector<T>, class Compare = AZStd::less<typename Container::value_type>>
|
||||
using priority_queue = std::priority_queue<T, Container, Compare>;
|
||||
}
|
||||
|
||||
#endif // AZSTD_QUEUE_H
|
||||
#pragma once
|
||||
|
||||
@@ -5,103 +5,13 @@
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#ifndef AZSTD_STACK_H
|
||||
#define AZSTD_STACK_H 1
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/std/containers/deque.h>
|
||||
#include <stack>
|
||||
|
||||
namespace AZStd
|
||||
{
|
||||
/**
|
||||
* Stack container is complaint with \ref CStd (23.2.3.3)
|
||||
* The only extension we have is that we allow access
|
||||
* to the underlying container via: get_container function.
|
||||
* Check the stack \ref AZStdExamples.
|
||||
*/
|
||||
template<class T, class Container = AZStd::deque<T> >
|
||||
class stack
|
||||
{
|
||||
enum
|
||||
{
|
||||
CONTAINER_VERSION = 1
|
||||
};
|
||||
public:
|
||||
typedef stack<T, Container> this_type;
|
||||
typedef Container container_type;
|
||||
typedef typename Container::value_type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
typedef typename Container::reference reference;
|
||||
typedef typename Container::const_reference const_reference;
|
||||
|
||||
AZ_FORCE_INLINE stack() {}
|
||||
AZ_FORCE_INLINE explicit stack(const container_type& container)
|
||||
: m_container(container) {}
|
||||
AZ_FORCE_INLINE bool empty() const { return m_container.empty(); }
|
||||
AZ_FORCE_INLINE size_type size() const { return m_container.size(); }
|
||||
AZ_FORCE_INLINE reference top() { return m_container.back(); }
|
||||
AZ_FORCE_INLINE const_reference top() const { return m_container.back(); }
|
||||
AZ_FORCE_INLINE reference back() { return m_container.back(); }
|
||||
AZ_FORCE_INLINE const_reference back() const { return m_container.back(); }
|
||||
AZ_FORCE_INLINE void push(const value_type& value) { m_container.push_back(value); }
|
||||
AZ_FORCE_INLINE void pop() { m_container.pop_back(); }
|
||||
AZ_FORCE_INLINE void push() { m_container.push_back(); }
|
||||
|
||||
AZ_FORCE_INLINE stack(this_type&& rhs)
|
||||
: m_container(AZStd::move(rhs.m_container)) {}
|
||||
AZ_FORCE_INLINE explicit stack(Container&& container)
|
||||
: m_container(AZStd::move(container)) {}
|
||||
this_type& operator=(this_type&& rhs) { m_container = AZStd::move(rhs.m_container); return *this; }
|
||||
void push(value_type&& value) { m_container.push_back(AZStd::move(value)); }
|
||||
template<class Args>
|
||||
void emplace(Args&& args) { m_container.emplace_back(AZStd::forward<Args>(args)); }
|
||||
void swap(this_type&& rhs) { m_container.swap(AZStd::move(rhs.m_container)); }
|
||||
|
||||
void swap(this_type& rhs) { AZStd::swap(m_container, rhs.m_container); }
|
||||
|
||||
AZ_FORCE_INLINE Container& get_container() { return m_container; }
|
||||
AZ_FORCE_INLINE const Container& get_container() const { return m_container; }
|
||||
|
||||
protected:
|
||||
Container m_container;
|
||||
};
|
||||
|
||||
// queue TEMPLATE FUNCTIONS
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator==(const AZStd::stack<T, Container>& left, const AZStd::stack<T, Container>& right)
|
||||
{
|
||||
return left.get_container() == right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator!=(const AZStd::stack<T, Container>& left, const AZStd::stack<T, Container>& right)
|
||||
{
|
||||
return left.get_container() != right.get_container();
|
||||
}
|
||||
|
||||
/* template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator<(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() < right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() > right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE operator<=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() <= right.get_container();
|
||||
}
|
||||
|
||||
template<class T, class Container>
|
||||
AZ_FORCE_INLINE bool operator>=(const queue<T,Container>& left, const queue<T,Container>& right)
|
||||
{
|
||||
return left.get_container() >= right.get_container();
|
||||
}*/
|
||||
template<class T, class Container = AZStd::deque<T>>
|
||||
using stack = std::stack<T, Container>;
|
||||
}
|
||||
|
||||
#endif // AZSTD_STACK_H
|
||||
#pragma once
|
||||
|
||||
@@ -298,7 +298,7 @@ namespace UnitTest
|
||||
AZ_TEST_ASSERT(int_queue.empty());
|
||||
AZ_TEST_ASSERT(int_queue.size() == 0);
|
||||
|
||||
// Queue uses deque as default container, so try to contruct to queue from a deque.
|
||||
// Queue uses deque as default container, so try to construct to queue from a deque.
|
||||
deque<int> container(40, 10);
|
||||
int_queue_type int_queue2(container);
|
||||
AZ_TEST_ASSERT(!int_queue2.empty());
|
||||
@@ -324,7 +324,7 @@ namespace UnitTest
|
||||
AZ_TEST_ASSERT(int_queue2.size() == 40);
|
||||
AZ_TEST_ASSERT(int_queue2.back() == 20);
|
||||
|
||||
int_queue.push();
|
||||
int_queue.emplace();
|
||||
AZ_TEST_ASSERT(!int_queue.empty());
|
||||
AZ_TEST_ASSERT(int_queue.size() == 1);
|
||||
|
||||
@@ -423,7 +423,7 @@ namespace UnitTest
|
||||
AZ_TEST_ASSERT(int_stack2.size() == 40);
|
||||
AZ_TEST_ASSERT(int_stack2.top() == 10);
|
||||
|
||||
int_stack.push();
|
||||
int_stack.emplace();
|
||||
AZ_TEST_ASSERT(!int_stack.empty());
|
||||
AZ_TEST_ASSERT(int_stack.size() == 1);
|
||||
// StackContainerTest-End
|
||||
@@ -669,4 +669,19 @@ namespace UnitTest
|
||||
++iteration;
|
||||
}
|
||||
}
|
||||
|
||||
using StackContainerTestFixture = ScopedAllocatorSetupFixture;
|
||||
|
||||
TEST_F(StackContainerTestFixture, StackEmplaceOperator_SupportsZeroOrMoreArguments)
|
||||
{
|
||||
using TestPairType = AZStd::pair<int, int>;
|
||||
AZStd::stack<TestPairType> testStack;
|
||||
testStack.emplace();
|
||||
testStack.emplace(1);
|
||||
testStack.emplace(2, 3);
|
||||
|
||||
using ContainerType = typename AZStd::stack<TestPairType>::container_type;
|
||||
AZStd::stack<TestPairType> expectedStack(ContainerType{ TestPairType{ 0, 0 }, TestPairType{ 1, 0 }, TestPairType{ 2, 3 } });
|
||||
EXPECT_EQ(expectedStack, testStack);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user