Files
UnrealEngineUWP/Engine/Source/Programs/PixelStreaming/SessionMonitor/src/Monitor.cpp
Ryan Durand 9ef3748747 Updating copyrights for Engine Programs.
#rnx
#rb none
#jira none

#ROBOMERGE-OWNER: ryan.durand
#ROBOMERGE-AUTHOR: ryan.durand
#ROBOMERGE-SOURCE: CL 10869242 in //Fortnite/Release-12.00/... via CL 10869536
#ROBOMERGE-BOT: FORTNITE (Main -> Dev-EngineMerge) (v613-10869866)

[CL 10870955 by Ryan Durand in Main branch]
2019-12-26 23:01:54 -05:00

262 lines
6.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "SessionMonitorPCH.h"
#include "Monitor.h"
#include "Logging.h"
#include "Algorithm.h"
#include "Spawner.h"
#include "StringUtils.h"
#include "AppSession.h"
#include "Config.h"
#include "ScopeGuard.h"
FMonitor::FMonitor(boost::asio::io_context& IOContext, std::vector<FAppConfig> Cfg)
: IOContext(IOContext)
, Cfg(std::move(Cfg))
{
static FDummyMonitorEventListener DummyListener;
EventListener = &DummyListener;
DummyWork = std::make_unique<boost::asio::io_context::work>(IOContext);
}
FMonitor::~FMonitor()
{
verify(Sessions.size() == 0);
}
void FMonitor::StartImpl()
{
CHECK_MAINTHREAD();
bRestartingAll = false;
auto StartFailedGuard = ScopeGuard([&]
{
if (State == EState::Starting)
{
State = EState::None;
}
EventListener->OnStartFailed();
});
if (State!=EState::None)
{
EG_LOG(LogDefault, Warning, "Can't start, because there is already a session running");
return;
}
verify(Sessions.size() == 0);
State = EState::Starting;
// Start acceptor if required
if (!AppAcceptor)
{
// NOTE: Specifying port as 0, will let the OS pick an ephemeral port
boost::asio::ip::tcp::endpoint Endpoint(boost::asio::ip::tcp::v4(), 0);
auto TmpAcceptor = std::make_shared<boost::asio::ip::tcp::acceptor>(IOContext);
TmpAcceptor->open(Endpoint.protocol());
boost::system::error_code Ec;
TmpAcceptor->bind(Endpoint, Ec);
if (Ec)
{
EG_LOG(LogDefault, Error, "Error binding acceptor. Reason=%s", Ec.message().c_str());
return;
}
TmpAcceptor->listen(1, Ec);
if (Ec)
{
EG_LOG(LogDefault, Error, "Error calling listen on the acceptor. Reason=%s", Ec.message().c_str());
return;
}
TmpAcceptor->non_blocking(true, Ec);
if (Ec)
{
EG_LOG(LogDefault, Error, "Could not set Acceptor to non-blocking. Reason=%s", Ec.message().c_str());
return;
}
AppAcceptor = TmpAcceptor;
EG_LOG(LogDefault, Log, "Using port %d for communicating with child apps", (int)GetMonitoringPort());
}
//
// Start all applications
//
// Launching and starting timeout detection is done in separate steps, so we
// can launch all apps and not worry about one launched app timing out because we are still launching
// another one that takes a long time to launch by design.
//
std::vector<std::shared_ptr<FAppSession>> All;
for (FAppConfig& AppCfg : Cfg)
{
auto Session = std::make_shared<FAppSession>(*this, &AppCfg);
bool Res = Session->Launch(*AppAcceptor);
if (!Res)
{
for (FAppSession* Session : Sessions)
{
Session->Shutdown();
}
return;
}
All.push_back(Session); // Keeping a strong reference, while we launch all the other apps
Sessions.push_back(Session.get());
}
//
// Now that all are launched, initiate the timeout detection
for (FAppSession* Session : Sessions)
{
Session->StartTimeoutDetection();
}
StartFailedGuard.Dismiss();
EG_LOG(LogDefault, Log, "Raising event START.");
EventListener->OnStart();
State = EState::Running;
}
void FMonitor::Start()
{
boost::asio::post(IOContext, [this]()
{
StartImpl();
});
}
void FMonitor::StopImpl(bool bShutdownMonitor)
{
CHECK_MAINTHREAD();
if (State==EState::None)
{
EG_LOG(LogDefault, Warning, "Can't initiate stop, since there is no session running");
}
else if (State==EState::Starting)
{
EG_LOG(LogDefault, Warning, "Can't initiate stop when starting");
}
else if (State == EState::Running)
{
EG_LOG(LogDefault, Log, "Initiating stop");
State = EState::Stopping;
for (FAppSession* Session : Sessions)
{
Session->Shutdown();
}
}
else if (State == EState::Stopping)
{
EG_LOG(LogDefault, Warning, "Can't initiate stop, since it's stopping already");
}
if (bShutdownMonitor)
{
DummyWork.reset();
}
}
void FMonitor::Stop(bool bShutdownMonitor)
{
boost::asio::post(IOContext, [this, bShutdownMonitor]()
{
StopImpl(bShutdownMonitor);
});
}
void FMonitor::ForgetSession(FAppSession* App)
{
CHECK_MAINTHREAD();
Erase(Sessions, App);
bool doAppCrashAction = false;
switch (App->GetExitReason())
{
case FAppSession::EExitReason::None:
case FAppSession::EExitReason::RequestedShutdown:
// Nothing to do
break;
case FAppSession::EExitReason::KilledAfterFreeze:
EG_LOG(LogDefault, Log, "Raising event APPFROZE.");
EventListener->OnAppFroze(App->GetCfg());
doAppCrashAction = true;
break;
case FAppSession::EExitReason::Unexpected:
EG_LOG(LogDefault, Log, "Raising event APPCRASHED.");
EventListener->OnAppCrashed(App->GetCfg());
doAppCrashAction = true;
break;
default:
// Nothing to do
verify(0);
}
if (doAppCrashAction && State == EState::Running)
{
DoAppCrashAction(App);
}
if ((Sessions.size() == 0) && ((State == EState::Running) || (State == EState::Stopping)))
{
EG_LOG(LogDefault, Log, "All apps terminated. Raising event STOP.");
EventListener->OnStop();
State = EState::None;
if (bRestartingAll)
{
StartImpl();
}
}
}
void FMonitor::SetEventListener(IMonitorEventListener* EventListener_)
{
CHECK_MAINTHREAD();
EventListener = EventListener_;
}
void FMonitor::DoAppCrashAction(FAppSession* App)
{
CHECK_MAINTHREAD();
switch (App->GetCfg()->OnCrashAction)
{
case EAppCrashAction::None:
EG_LOG(LogDefault, Log, "No app oncrash action to perform");
break;
case EAppCrashAction::StopSession:
EG_LOG(LogDefault, Log, "Performing 'StopSession' oncrash action");
if (State != EState::Stopping)
{
StopImpl(false);
}
break;
case EAppCrashAction::RestartApp:
{
EG_LOG(LogDefault, Log, "Performing 'RestartApp' oncrash action");
auto Session = std::make_shared<FAppSession>(*this, App->GetCfg());
bool res = Session->Launch(*AppAcceptor);
if (!res)
{
EG_LOG(LogDefault, Error, "App restart failed. Shutting down session...");
if (State != EState::Stopping)
{
StopImpl(false);
}
}
Sessions.push_back(Session.get());
Session->StartTimeoutDetection();
}
break;
case EAppCrashAction::RestartSession:
EG_LOG(LogDefault, Log, "Performing 'RestartSession' oncrash action");
if (!bRestartingAll)
{
bRestartingAll = true;
if (State != EState::Stopping)
{
StopImpl(false);
}
}
break;
}
}