Files
UnrealEngineUWP/Engine/Source/Runtime/Online/HTTP/Private/HttpThread.cpp
James Hopkin 2addd2c688 Copying //UE4/Dev-Online to //UE4/Dev-Main (Source: //UE4/Dev-Online @ 4040378)
#lockdown Nick.Penwarden

============================
  MAJOR FEATURES & CHANGES
============================

Change 3661955 by Rob.Cannaday

	Change access pattern to FHttpThread to remove critical section
	#http #ue4

Change 3672463 by Rob.Cannaday

	Revert Curl/WinINet HTTP implementation of GetHeader/GetAllHeaders to not allowing access until the request is complete
	Some optimizations to Curl/WinINet GetAllHeaders
	Add bIsAsyncProcessingFinished to FHttpResponseWinInet, set when the request is finished on the HTTP thread, and only set bIsReady when we have finished processing all received headers
	Use FThreadSafeBool instead of volatile int32 in WinINet HTTP

Change 3855724 by Michael.Kirzinger

	Fix include paths

Change 3949903 by Ian.Fox

	#OnlineSubsystemSteam - Add IsValid check to SteamSessionInfo to fix a rare crash
	- Misc cleanup / casting fixes
	#github #4532

Change 3949914 by Ian.Fox

	#ShooterGame - Wait for session creation to finish before cleaning up session on network errors
	#review-3831601

Change 3662317 by Rob.Cannaday

	Add delegate for when we receive an http header
	Add FHttpRequestImpl to implement simple virtual functions that all platforms implement in the same way
	Add lock access to Curl/WinInet response headers access so game thread can safely read while processing
	#jira OGS-832
	#jira OGS-833
	#http #ue4

#rb none

[CL 4040611 by James Hopkin in Main branch]
2018-04-30 13:57:29 -04:00

225 lines
6.0 KiB
C++

// Copyright 1998-2018 Epic Games, Inc. All Rights Reserved.
#include "HttpThread.h"
#include "IHttpThreadedRequest.h"
#include "HAL/PlatformTime.h"
#include "HAL/PlatformProcess.h"
#include "HAL/RunnableThread.h"
#include "HttpModule.h"
#include "Http.h"
// FHttpThread
FHttpThread::FHttpThread()
: Thread(nullptr)
{
HttpThreadActiveFrameTimeInSeconds = FHttpModule::Get().GetHttpThreadActiveFrameTimeInSeconds();
HttpThreadActiveMinimumSleepTimeInSeconds = FHttpModule::Get().GetHttpThreadActiveMinimumSleepTimeInSeconds();
HttpThreadIdleFrameTimeInSeconds = FHttpModule::Get().GetHttpThreadIdleFrameTimeInSeconds();
HttpThreadIdleMinimumSleepTimeInSeconds = FHttpModule::Get().GetHttpThreadIdleMinimumSleepTimeInSeconds();
UE_LOG(LogHttp, Log, TEXT("HTTP thread active frame time %.1f ms. Minimum active sleep time is %.1f ms. HTTP thread idle frame time %.1f ms. Minimum idle sleep time is %.1f ms."), HttpThreadActiveFrameTimeInSeconds * 1000.0, HttpThreadActiveMinimumSleepTimeInSeconds * 1000.0, HttpThreadIdleFrameTimeInSeconds * 1000.0, HttpThreadIdleMinimumSleepTimeInSeconds * 1000.0);
}
FHttpThread::~FHttpThread()
{
StopThread();
}
void FHttpThread::StartThread()
{
Thread = FRunnableThread::Create(this, TEXT("HttpManagerThread"), 128 * 1024, TPri_Normal);
}
void FHttpThread::StopThread()
{
if (Thread != nullptr)
{
Thread->Kill(true);
delete Thread;
Thread = nullptr;
}
}
void FHttpThread::AddRequest(IHttpThreadedRequest* Request)
{
PendingThreadedRequests.Enqueue(Request);
}
void FHttpThread::CancelRequest(IHttpThreadedRequest* Request)
{
CancelledThreadedRequests.Enqueue(Request);
}
void FHttpThread::GetCompletedRequests(TArray<IHttpThreadedRequest*>& OutCompletedRequests)
{
check(IsInGameThread());
IHttpThreadedRequest* Request = nullptr;
while (CompletedThreadedRequests.Dequeue(Request))
{
OutCompletedRequests.Add(Request);
}
}
bool FHttpThread::Init()
{
LastTime = FPlatformTime::Seconds();
return true;
}
uint32 FHttpThread::Run()
{
// Arrays declared outside of loop to re-use memory
TArray<IHttpThreadedRequest*> RequestsToCancel;
TArray<IHttpThreadedRequest*> RequestsToStart;
TArray<IHttpThreadedRequest*> RequestsToComplete;
while (!ExitRequest.GetValue())
{
double OuterLoopBegin = FPlatformTime::Seconds();
double OuterLoopEnd = 0.0;
bool bKeepProcessing = true;
while (bKeepProcessing)
{
double InnerLoopBegin = FPlatformTime::Seconds();
Process(RequestsToCancel, RequestsToStart, RequestsToComplete);
if (RunningThreadedRequests.Num() == 0)
{
bKeepProcessing = false;
}
double InnerLoopEnd = FPlatformTime::Seconds();
if (bKeepProcessing)
{
double InnerLoopTime = InnerLoopEnd - InnerLoopBegin;
double InnerSleep = FMath::Max(HttpThreadActiveFrameTimeInSeconds - InnerLoopTime, HttpThreadActiveMinimumSleepTimeInSeconds);
FPlatformProcess::SleepNoStats(InnerSleep);
}
else
{
OuterLoopEnd = InnerLoopEnd;
}
}
double OuterLoopTime = OuterLoopEnd - OuterLoopBegin;
double OuterSleep = FMath::Max(HttpThreadIdleFrameTimeInSeconds - OuterLoopTime, HttpThreadIdleMinimumSleepTimeInSeconds);
FPlatformProcess::SleepNoStats(OuterSleep);
}
return 0;
}
void FHttpThread::Tick()
{
TArray<IHttpThreadedRequest*> RequestsToCancel;
TArray<IHttpThreadedRequest*> RequestsToStart;
TArray<IHttpThreadedRequest*> RequestsToComplete;
Process(RequestsToCancel, RequestsToStart, RequestsToComplete);
}
void FHttpThread::HttpThreadTick(float DeltaSeconds)
{
// empty
}
bool FHttpThread::StartThreadedRequest(IHttpThreadedRequest* Request)
{
return Request->StartThreadedRequest();
}
void FHttpThread::CompleteThreadedRequest(IHttpThreadedRequest* Request)
{
// empty
}
void FHttpThread::Process(TArray<IHttpThreadedRequest*>& RequestsToCancel, TArray<IHttpThreadedRequest*>& RequestsToStart, TArray<IHttpThreadedRequest*>& RequestsToComplete)
{
// cache all cancelled and pending requests
{
IHttpThreadedRequest* Request = nullptr;
RequestsToCancel.Reset();
while (CancelledThreadedRequests.Dequeue(Request))
{
RequestsToCancel.Add(Request);
}
RequestsToStart.Reset();
while (PendingThreadedRequests.Dequeue(Request))
{
RequestsToStart.Add(Request);
}
}
// Cancel any pending cancel requests
for (IHttpThreadedRequest* Request : RequestsToCancel)
{
if (RunningThreadedRequests.Remove(Request) > 0)
{
RequestsToComplete.Add(Request);
}
}
const double AppTime = FPlatformTime::Seconds();
const double ElapsedTime = AppTime - LastTime;
LastTime = AppTime;
// Tick any running requests
// as long as they properly finish in HttpThreadTick below they are unaffected by a possibly large ElapsedTime above
for (IHttpThreadedRequest* Request : RunningThreadedRequests)
{
Request->TickThreadedRequest(ElapsedTime);
}
// Start any pending requests
// Tick new requests separately from existing RunningThreadedRequests so they get a chance
// to send unaffected by possibly large ElapsedTime above
for (IHttpThreadedRequest* Request : RequestsToStart)
{
if (StartThreadedRequest(Request))
{
RunningThreadedRequests.Add(Request);
Request->TickThreadedRequest(0.0f);
}
else
{
RequestsToComplete.Add(Request);
}
}
// Every valid request in RunningThreadedRequests gets at least two calls to HttpThreadTick
// Blocking loads still can affect things if the network stack can't keep its connections alive
HttpThreadTick(ElapsedTime);
// Move any completed requests
for (int32 Index = 0; Index < RunningThreadedRequests.Num(); ++Index)
{
IHttpThreadedRequest* Request = RunningThreadedRequests[Index];
if (Request->IsThreadedRequestComplete())
{
RequestsToComplete.Add(Request);
RunningThreadedRequests.RemoveAtSwap(Index);
--Index;
}
}
if (RequestsToComplete.Num() > 0)
{
for (IHttpThreadedRequest* Request : RequestsToComplete)
{
CompleteThreadedRequest(Request);
CompletedThreadedRequests.Enqueue(Request);
}
RequestsToComplete.Reset();
}
}
void FHttpThread::Stop()
{
ExitRequest.Set(true);
}
void FHttpThread::Exit()
{
// empty
}