General refactor of runtime classes.

This commit is contained in:
jonawals
2021-05-21 09:00:57 +01:00
parent 226ccce52d
commit d732b7d0ce
26 changed files with 320 additions and 175 deletions
@@ -13,77 +13,42 @@
#pragma once
#include <Process/JobRunner/TestImpactProcessJobInfo.h>
#include <Process/TestImpactProcessInfo.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/optional.h>
#include <Process/JobRunner/TestImpactProcessJobMeta.h>
namespace TestImpact
{
//! Result of a job that was run.
enum class JobResult
{
NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed).
FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid).
Terminated, //!< The job was terminated by the job runner (e.g. job or runner timeout exceeded while job was in-flight).
ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero).
ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero).
};
//! The meta-data for a given job.
struct JobMeta
{
JobResult m_result = JobResult::NotExecuted;
AZStd::optional<AZStd::chrono::high_resolution_clock::time_point>
m_startTime; //!< The time, relative to the job runner start, that this job started.
AZStd::optional<AZStd::chrono::milliseconds> m_duration; //!< The duration that this job took to complete.
AZStd::optional<ReturnCode> m_returnCode; //!< The return code of the underlying processes of this job.
};
//! Representation of a unit of work to be performed by a process.
//! @tparam JobInfoT The JobInfo structure containing the information required to run this job.
//! @tparam JobPayloadT The resulting output of the processed artifact produced by this job.
template<typename JobInfoT, typename JobPayloadT>
class Job
: public JobMetaContainer
{
public:
using Info = JobInfoT;
using Payload = JobPayloadT;
//! Constructor with r-values for the specific use case of the job runner.
Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional<Payload>&& payload);
Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional<Payload>&& payload);
//! Returns the job info associated with this job.
const Info& GetJobInfo() const;
//! Returns the result of this job.
JobResult GetResult() const;
//! Returns the start time, relative to the job runner start, that this job started.
AZStd::chrono::high_resolution_clock::time_point GetStartTime() const;
//! Returns the end time, relative to the job runner start, that this job ended.
AZStd::chrono::high_resolution_clock::time_point GetEndTime() const;
//! Returns the duration that this job took to complete.
AZStd::chrono::milliseconds GetDuration() const;
//! Returns the return code of the underlying processes of this job.
AZStd::optional<ReturnCode> GetReturnCode() const;
//! Returns the payload produced by this job.
const AZStd::optional<Payload>& GetPayload() const;
//! Facilitates the client consuming the payload.
AZStd::optional<Payload>&& ReleasePayload();
private:
Info m_jobInfo;
JobMeta m_meta;
AZStd::optional<Payload> m_payload;
};
template<typename JobInfoT, typename JobPayloadT>
Job<JobInfoT, JobPayloadT>::Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional<Payload>&& payload)
: m_jobInfo(jobInfo)
, m_meta(AZStd::move(jobMeta))
Job<JobInfoT, JobPayloadT>::Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional<Payload>&& payload)
: JobMetaContainer(AZStd::move(jobMeta))
, m_jobInfo(jobInfo)
, m_payload(AZStd::move(payload))
{
}
@@ -94,46 +59,15 @@ namespace TestImpact
return m_jobInfo;
}
template<typename JobInfoT, typename JobPayloadT>
JobResult Job<JobInfoT, JobPayloadT>::GetResult() const
{
return m_meta.m_result;
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::optional<ReturnCode> Job<JobInfoT, JobPayloadT>::GetReturnCode() const
{
return m_meta.m_returnCode;
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::chrono::high_resolution_clock::time_point Job<JobInfoT, JobPayloadT>::GetStartTime() const
{
return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point());
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::chrono::high_resolution_clock::time_point Job<JobInfoT, JobPayloadT>::GetEndTime() const
{
if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value())
{
return m_meta.m_startTime.value() + m_meta.m_duration.value();
}
else
{
return AZStd::chrono::high_resolution_clock::time_point();
}
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::chrono::milliseconds Job<JobInfoT, JobPayloadT>::GetDuration() const
{
return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{0});
}
template<typename JobInfoT, typename JobPayloadT>
const AZStd::optional<JobPayloadT>& Job<JobInfoT, JobPayloadT>::GetPayload() const
{
return m_payload;
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::optional<JobPayloadT>&& Job<JobInfoT, JobPayloadT>::ReleasePayload()
{
return AZStd::move(m_payload);
}
} // namespace TestImpact
@@ -24,6 +24,7 @@ namespace TestImpact
{
public:
using IdType = size_t;
using CommandType = AZStd::string;
//! Client-provided identifier to distinguish between different jobs.
//! @note Ids of different job types are not interchangeable.
@@ -32,30 +33,37 @@ namespace TestImpact
IdType m_value;
};
//! Command used my ProcessScheduler to execute this job.
//! @note Commands of different job types are not interchangeable.
struct Command
{
CommandType m_args;
};
//! Constructs the job information with any additional information required by the job.
//! @param jobId The client-provided unique identifier for the job.
//! @param args The arguments used to launch the process running the job.
//! @param command The command used to launch the process running the job.
//! @param additionalInfo The arguments to be provided to the additional information data structure.
template<typename... AdditionalInfoArgs>
JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo);
JobInfo(Id jobId, const Command& command, AdditionalInfoArgs&&... additionalInfo);
//! Returns the id of this job.
Id GetId() const;
//! Returns the command arguments used to execute this job.
const AZStd::string& GetArgs() const;
const Command& GetCommand() const;
private:
Id m_id;
AZStd::string m_args;
Command m_command;
};
template<typename AdditionalInfo>
template<typename... AdditionalInfoArgs>
JobInfo<AdditionalInfo>::JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo)
JobInfo<AdditionalInfo>::JobInfo(Id jobId, const Command& command, AdditionalInfoArgs&&... additionalInfo)
: AdditionalInfo{std::forward<AdditionalInfoArgs>(additionalInfo)...}
, m_id(jobId)
, m_args(args)
, m_command(command)
{
}
@@ -66,8 +74,8 @@ namespace TestImpact
}
template<typename AdditionalInfo>
const AZStd::string& JobInfo<AdditionalInfo>::GetArgs() const
const typename JobInfo<AdditionalInfo>::Command& JobInfo<AdditionalInfo>::GetCommand() const
{
return m_args;
return m_command;
}
} // namespace TestImpact
@@ -0,0 +1,61 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Process/JobRunner/TestImpactProcessJobMeta.h>
namespace TestImpact
{
JobMetaContainer::JobMetaContainer(const JobMeta& jobMeta)
: m_meta(jobMeta)
{
}
JobMetaContainer::JobMetaContainer(JobMeta&& jobMeta)
: m_meta(AZStd::move(jobMeta))
{
}
JobResult JobMetaContainer::GetJobResult() const
{
return m_meta.m_result;
}
AZStd::optional<ReturnCode> JobMetaContainer::GetReturnCode() const
{
return m_meta.m_returnCode;
}
AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetStartTime() const
{
return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point());
}
AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetEndTime() const
{
if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value())
{
return m_meta.m_startTime.value() + m_meta.m_duration.value();
}
else
{
return AZStd::chrono::high_resolution_clock::time_point();
}
}
AZStd::chrono::milliseconds JobMetaContainer::GetDuration() const
{
return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{ 0 });
}
} // namespace TestImpact
@@ -0,0 +1,68 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Process/TestImpactProcessInfo.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/optional.h>
namespace TestImpact
{
//! Result of a job that was run.
enum class JobResult
{
NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed).
FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid).
Timeout, //!< The job was terminated by the job runner (e.g. job timeout exceeded while job was in-flight).
Terminated, //!< The job was terminated by the job runner (e.g. global timeout exceeded while job was in-flight).
ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero).
ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero).
};
//! The meta-data for a given job.
struct JobMeta
{
JobResult m_result = JobResult::NotExecuted;
AZStd::optional<AZStd::chrono::high_resolution_clock::time_point>
m_startTime; //!< The time, relative to the job runner start, that this job started.
AZStd::optional<AZStd::chrono::milliseconds> m_duration; //!< The duration that this job took to complete.
AZStd::optional<ReturnCode> m_returnCode; //!< The return code of the underlying processes of this job.
};
class JobMetaContainer
{
public:
JobMetaContainer(const JobMeta& jobMeta);
JobMetaContainer(JobMeta&& jobMeta);
//! Returns the result of this job.
JobResult GetJobResult() const;
//! Returns the start time, relative to the job runner start, that this job started.
AZStd::chrono::high_resolution_clock::time_point GetStartTime() const;
//! Returns the end time, relative to the job runner start, that this job ended.
AZStd::chrono::high_resolution_clock::time_point GetEndTime() const;
//! Returns the duration that this job took to complete.
AZStd::chrono::milliseconds GetDuration() const;
//! Returns the return code of the underlying processes of this job.
AZStd::optional<ReturnCode> GetReturnCode() const;
private:
JobMeta m_meta;
};
} // namespace TestImpact
@@ -13,7 +13,6 @@
#pragma once
#include <Process/Scheduler/TestImpactProcessScheduler.h>
#include <TestImpactFramework/TestImpactCallback.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
@@ -27,7 +26,7 @@ namespace TestImpact
//! @param meta The meta-data about the job run.
//! @param std The standard output and standard error of the process running the job.
template<typename Job>
using JobCallback = AZStd::function<CallbackResult(const typename Job::Info& jobInfo, const JobMeta& meta, StdContent&& std)>;
using JobCallback = AZStd::function<ProcessCallbackResult(const typename Job::Info& jobInfo, const JobMeta& meta, StdContent&& std)>;
//! The payloads produced by the job-specific payload producer in the form of a map associating each job id with the job's payload.
template<typename Job>
@@ -116,7 +115,7 @@ namespace TestImpact
const auto* jobInfo = &jobInfos[jobIndex];
const auto jobId = jobInfo->GetId().m_value;
metas.emplace(jobId, AZStd::pair<JobMeta, const typename JobT::Info*>{JobMeta{}, jobInfo});
processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetArgs());
processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetCommand().m_args);
}
// Wrapper around low-level process launch callback to gather job meta-data and present a simplified callback interface to the client
@@ -134,7 +133,7 @@ namespace TestImpact
else
{
meta.m_startTime = createTime;
return CallbackResult::Continue;
return ProcessCallbackResult::Continue;
}
};
@@ -153,10 +152,14 @@ namespace TestImpact
{
meta.m_result = JobResult::ExecutedWithSuccess;
}
else if (exitCondition == ExitCondition::Terminated || exitCondition == ExitCondition::Timeout)
else if (exitCondition == ExitCondition::Terminated)
{
meta.m_result = JobResult::Terminated;
}
else if (exitCondition == ExitCondition::Timeout)
{
meta.m_result = JobResult::Timeout;
}
else
{
meta.m_result = JobResult::ExecutedWithFailure;
@@ -58,7 +58,7 @@ namespace TestImpact
for (auto& process : m_processPool)
{
if (PopAndLaunch(process) == CallbackResult::Abort)
if (PopAndLaunch(process) == ProcessCallbackResult::Abort)
{
TerminateAllProcesses(ExitCondition::Terminated);
return;
@@ -110,7 +110,7 @@ namespace TestImpact
const auto exitTime = AZStd::chrono::high_resolution_clock::now();
// Inform the client that the processes has exited
if (CallbackResult::Abort == m_processExitCallback(
if (ProcessCallbackResult::Abort == m_processExitCallback(
processId,
ExitCondition::Gracefull,
returnCode,
@@ -124,7 +124,7 @@ namespace TestImpact
else if (!m_processQueue.empty())
{
// This slot in the pool is free so launch one of the processes waiting in the queue
if (PopAndLaunch(processInFlight) == CallbackResult::Abort)
if (PopAndLaunch(processInFlight) == ProcessCallbackResult::Abort)
{
// Client chose to abort the scheduler
TerminateAllProcesses(ExitCondition::Terminated);
@@ -150,7 +150,7 @@ namespace TestImpact
const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value();
processInFlight.m_process.reset();
if (CallbackResult::Abort == m_processExitCallback(
if (ProcessCallbackResult::Abort == m_processExitCallback(
processId,
ExitCondition::Timeout,
returnCode,
@@ -172,7 +172,7 @@ namespace TestImpact
// Queue is empty, no more processes to launch
if (!m_processQueue.empty())
{
if (PopAndLaunch(processInFlight) == CallbackResult::Abort)
if (PopAndLaunch(processInFlight) == ProcessCallbackResult::Abort)
{
// Client chose to abort the scheduler
TerminateAllProcesses(ExitCondition::Terminated);
@@ -194,7 +194,7 @@ namespace TestImpact
}
}
CallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight)
ProcessCallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight)
{
auto processInfo = m_processQueue.front();
m_processQueue.pop();
@@ -251,7 +251,7 @@ namespace TestImpact
if (isCallingBackToClient)
{
const auto exitTime = AZStd::chrono::high_resolution_clock::now();
if (CallbackResult::Abort == m_processExitCallback(
if (ProcessCallbackResult::Abort == m_processExitCallback(
processInFlight.m_process->GetProcessInfo().GetId(),
exitStatus,
returnCode,
@@ -12,11 +12,10 @@
#pragma once
#include <TestImpactFramework/TestImpactRuntime.h>
#include <Process/TestImpactProcessInfo.h>
#include <TestImpactFramework/TestImpactCallback.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/containers/vector.h>
@@ -43,12 +42,19 @@ namespace TestImpact
Timeout = ProcessTimeoutErrorCode //!< The process was terminated by the scheduler due to exceeding runtime limit.
};
//! Client result for process scheduler callbacks.
enum class ProcessCallbackResult : bool
{
Continue, //!< Continune scheduling.
Abort //!< Abort scheduling immediately.
};
//! Callback for process launch attempt.
//! @param processId The id of the process that attempted to launch.
//! @param launchResult The result of the process launch attempt.
//! @param createTime The timestamp of the process launch attempt.
using ProcessLaunchCallback =
AZStd::function<CallbackResult(
AZStd::function<ProcessCallbackResult(
ProcessId processId,
LaunchResult launchResult,
AZStd::chrono::high_resolution_clock::time_point createTime)>;
@@ -60,7 +66,7 @@ namespace TestImpact
//! @param std The standard output and standard error of the process.
//! @param createTime The timestamp of the process exit.
using ProcessExitCallback =
AZStd::function<CallbackResult(
AZStd::function<ProcessCallbackResult(
ProcessId processId,
ExitCondition exitStatus,
ReturnCode returnCode,
@@ -94,7 +100,7 @@ namespace TestImpact
struct ProcessInFlight;
void MonitorProcesses();
CallbackResult PopAndLaunch(ProcessInFlight& processInFlight);
ProcessCallbackResult PopAndLaunch(ProcessInFlight& processInFlight);
void TerminateAllProcesses(ExitCondition exitStatus);
StdContent ConsumeProcessStdContent(ProcessInFlight& processInFlight);
void AccumulateProcessStdContent(ProcessInFlight& processInFlight);
@@ -15,7 +15,7 @@
namespace TestImpact
{
ProcessInfo::ProcessInfo(ProcessId id, const AZ::IO::Path& processPath, const AZStd::string& startupArgs)
ProcessInfo::ProcessInfo(ProcessId id, const RepoPath& processPath, const AZStd::string& startupArgs)
: m_id(id)
, m_parentHasStdOutput(false)
, m_parentHasStdErr(false)
@@ -29,7 +29,7 @@ namespace TestImpact
ProcessId id,
StdOutputRouting stdOut,
StdErrorRouting stdErr,
const AZ::IO::Path& processPath,
const RepoPath& processPath,
const AZStd::string& startupArgs)
: m_id(id)
, m_processPath(processPath)
@@ -45,7 +45,7 @@ namespace TestImpact
return m_id;
}
const AZ::IO::Path& ProcessInfo::GetProcessPath() const
const RepoPath& ProcessInfo::GetProcessPath() const
{
return m_processPath;
}
@@ -12,7 +12,8 @@
#pragma once
#include <AzCore/IO/Path/Path.h>
#include <TestImpactFramework/TestImpactRuntime.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/string/string.h>
@@ -64,9 +65,9 @@ namespace TestImpact
ProcessId processId,
StdOutputRouting stdOut,
StdErrorRouting stdErr,
const AZ::IO::Path& processPath,
const RepoPath& processPath,
const AZStd::string& startupArgs = "");
ProcessInfo(ProcessId processId, const AZ::IO::Path& processPath, const AZStd::string& startupArgs = "");
ProcessInfo(ProcessId processId, const RepoPath& processPath, const AZStd::string& startupArgs = "");
//! Returns the identifier of this process.
ProcessId GetId() const;
@@ -78,7 +79,7 @@ namespace TestImpact
bool ParentHasStdError() const;
// Returns the path to the process binary.
const AZ::IO::Path& GetProcessPath() const;
const RepoPath& GetProcessPath() const;
//! Returns the command line arguments used to launch the process.
const AZStd::string& GetStartupArgs() const;
@@ -87,7 +88,7 @@ namespace TestImpact
const ProcessId m_id;
const bool m_parentHasStdOutput;
const bool m_parentHasStdErr;
const AZ::IO::Path m_processPath;
const RepoPath m_processPath;
const AZStd::string m_startupArgs;
};
} // namespace TestImpact