Files
UnrealEngine/Engine/Source/Runtime/Online/HTTP/Private/EventLoopHttpThread.cpp
2025-05-18 13:04:45 +08:00

198 lines
5.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "EventLoopHttpThread.h"
#include "EventLoop/EventLoop.h"
#include "EventLoop/EventLoopIOManagerNull.h"
#include "GenericPlatform/HttpRequestCommon.h"
#include "Http.h"
#include "PlatformHttp.h"
#include "Stats/Stats.h"
class FHttpTaskTimerHandleEventLoop : public IHttpTaskTimerHandle
{
public:
FHttpTaskTimerHandleEventLoop(UE::EventLoop::FTimerHandle InHandle)
: Handle(InHandle)
{
}
virtual void RemoveTaskFrom(FHttpThreadBase* HttpThreadBase)
{
HttpThreadBase->RemoveTimerHandle(Handle);
}
private:
UE::EventLoop::FTimerHandle Handle;
};
FEventLoopHttpThread::FEventLoopHttpThread()
{
FPlatformHttp::AddDefaultUserAgentProjectComment(TEXT("http-eventloop"));
}
FEventLoopHttpThread::~FEventLoopHttpThread()
{
}
void FEventLoopHttpThread::StartThread()
{
CreateEventLoop();
ResetTickTimer();
FHttpThreadBase::StartThread();
}
void FEventLoopHttpThread::StopThread()
{
UE::EventLoop::IEventLoop* EventLoop = GetEventLoop();
if (EventLoop)
{
UE::EventLoop::TEventLoop<UE::EventLoop::FIOManagerNull> ShutdownEventLoop;
verify(ShutdownEventLoop.Init());
// Request shutdown for the http event loop, which will trigger shutdown of the
// ShutdownEventLoop on completion.
EventLoop->RequestShutdown([&ShutdownEventLoop]() {
ShutdownEventLoop.RequestShutdown();
});
// Set timer for shutdown duration warning.
const FTimespan ShutdownWarnTime = FTimespan::FromSeconds(10);
const double ShutdownStartTime = FPlatformTime::Seconds();
ShutdownEventLoop.SetTimer([ShutdownStartTime]()
{
UE_LOG(LogHttp, Warning, TEXT("Still waiting for event loop shutdown. Elapsed time: %f seconds"), FPlatformTime::Seconds() - ShutdownStartTime);
},
ShutdownWarnTime,
true /* repeat */);
if (NeedsSingleThreadTick())
{
// Set timer to poll http event loop.
const FTimespan HttpEventLoopTickFrequency = FTimespan::FromMilliseconds(1);
ShutdownEventLoop.SetTimer([EventLoop]()
{
// Run the http event loop waiting 0ms for IO.
EventLoop->RunOnce(FTimespan::Zero());
},
HttpEventLoopTickFrequency,
true /* repeat */);
}
// Wait for loop to shutdown.
ShutdownEventLoop.Run();
}
// Stop the thread before destroying the event loop to give the loop a chance to return from 'Run'.
FHttpThreadBase::StopThread();
if (EventLoop)
{
DestroyEventLoop();
}
}
void FEventLoopHttpThread::UpdateConfigs()
{
ResetTickTimer();
UpdateEventLoopConfigs();
FHttpThreadBase::UpdateConfigs();
}
void FEventLoopHttpThread::AddRequest(FHttpRequestCommon* Request)
{
FHttpThreadBase::AddRequest(Request);
if (UE::EventLoop::IEventLoop* EventLoop = GetEventLoop())
{
// Force a wakeup to process new tasks.
EventLoop->PostAsyncTask([this]()
{
TArray<FHttpRequestCommon*> RequestsToCancel;
TArray<FHttpRequestCommon*> RequestsToComplete;
Process(RequestsToCancel, RequestsToComplete);
});
}
}
void FEventLoopHttpThread::CancelRequest(FHttpRequestCommon* Request)
{
FHttpThreadBase::CancelRequest(Request);
if (UE::EventLoop::IEventLoop* EventLoop = GetEventLoop())
{
// Force a wakeup to process new tasks.
EventLoop->PostAsyncTask([this]()
{
TArray<FHttpRequestCommon*> RequestsToCancel;
TArray<FHttpRequestCommon*> RequestsToComplete;
Process(RequestsToCancel, RequestsToComplete);
});
}
}
void FEventLoopHttpThread::GetCompletedRequests(TArray<FHttpRequestCommon*>& OutCompletedRequests)
{
FHttpThreadBase::GetCompletedRequests(OutCompletedRequests);
}
void FEventLoopHttpThread::Tick()
{
FHttpThreadBase::Tick();
if (ensure(NeedsSingleThreadTick()))
{
// Run the http event loop waiting 0ms for IO.
GetEventLoopChecked().RunOnce(FTimespan::Zero());
}
}
bool FEventLoopHttpThread::Init()
{
if (!GetEventLoopChecked().Init())
{
UE_LOG(LogHttp, Error, TEXT("Failed to initialize the event loop."));
return false;
}
return FHttpThreadBase::Init();
}
uint32 FEventLoopHttpThread::Run()
{
GetEventLoopChecked().Run();
return 0;
}
void FEventLoopHttpThread::ResetTickTimer()
{
UE::EventLoop::IEventLoop& EventLoop = GetEventLoopChecked();
EventLoop.ClearTimer(RequestTickTimer);
RequestTickTimer = EventLoop.SetTimer([this]()
{
TArray<FHttpRequestCommon*> RequestsToCancel;
TArray<FHttpRequestCommon*> RequestsToComplete;
Process(RequestsToCancel, RequestsToComplete);
},
FTimespan::FromSeconds(FHttpModule::Get().GetHttpEventLoopThreadTickIntervalInSeconds()),
true /* repeat */);
}
TSharedPtr<IHttpTaskTimerHandle> FEventLoopHttpThread::AddHttpThreadTask(TFunction<void()>&& Task, float InDelay)
{
UE::EventLoop::IEventLoop& EventLoop = GetEventLoopChecked();
return MakeShared<FHttpTaskTimerHandleEventLoop>(EventLoop.SetTimer(Task, FTimespan::FromSeconds(InDelay)));
}
void FEventLoopHttpThread::RemoveTimerHandle(FTSTicker::FDelegateHandle DelegateHandle)
{
checkNoEntry();
}
void FEventLoopHttpThread::RemoveTimerHandle(UE::EventLoop::FTimerHandle EventLoopTimerHandle)
{
UE::EventLoop::IEventLoop& EventLoop = GetEventLoopChecked();
EventLoop.ClearTimer(EventLoopTimerHandle);
}