Files
UnrealEngine/Engine/Source/Developer/DirectoryWatcher/Private/Windows/DirectoryWatcherWindows.cpp
2025-05-18 13:04:45 +08:00

156 lines
4.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "DirectoryWatcherWindows.h"
#include "DirectoryWatcherPrivate.h"
FDirectoryWatcherWindows::FDirectoryWatcherWindows()
{
NumRequests = 0;
}
FDirectoryWatcherWindows::~FDirectoryWatcherWindows()
{
if ( RequestMap.Num() != 0 )
{
// Delete any remaining requests here. These requests are likely from modules which are still loaded at the time that this module unloads.
for (TMap<FDirectoryWithFlags, FDirectoryWatchRequestWindows*>::TConstIterator RequestIt(RequestMap); RequestIt; ++RequestIt)
{
if ( ensure(RequestIt.Value()) )
{
// make sure we end the watch request, as we may get a callback if a request is in flight
RequestIt.Value()->EndWatchRequest();
delete RequestIt.Value();
NumRequests--;
}
}
RequestMap.Empty();
}
if ( RequestsPendingDelete.Num() != 0 )
{
for ( int32 RequestIdx = 0; RequestIdx < RequestsPendingDelete.Num(); ++RequestIdx )
{
delete RequestsPendingDelete[RequestIdx];
NumRequests--;
}
}
// Make sure every request that was created is destroyed
ensure(NumRequests == 0);
}
bool FDirectoryWatcherWindows::RegisterDirectoryChangedCallback_Handle( const FString& Directory, const FDirectoryChanged& InDelegate, FDelegateHandle& Handle, uint32 Flags )
{
const FDirectoryWithFlags DirectoryKey(Directory, Flags);
FDirectoryWatchRequestWindows** RequestPtr = RequestMap.Find(DirectoryKey);
FDirectoryWatchRequestWindows* Request = NULL;
if ( RequestPtr )
{
// There should be no NULL entries in the map
check (*RequestPtr);
Request = *RequestPtr;
}
else
{
Request = new FDirectoryWatchRequestWindows(Flags);
NumRequests++;
// Begin reading directory changes
if ( !Request->Init(Directory) )
{
uint32 Error = GetLastError();
TCHAR ErrorMsg[1024];
FPlatformMisc::GetSystemErrorMessage(ErrorMsg, 1024, Error);
UE_LOG(LogDirectoryWatcher, Warning, TEXT("Failed to begin reading directory changes for %s. Error: %s (0x%08x)"), *Directory, ErrorMsg, Error);
delete Request;
NumRequests--;
return false;
}
RequestMap.Add(DirectoryKey, Request);
}
Handle = Request->AddDelegate(InDelegate);
return true;
}
bool FDirectoryWatcherWindows::UnregisterDirectoryChangedCallback_Handle( const FString& Directory, FDelegateHandle InHandle )
{
for (const auto& RequestPair : RequestMap)
{
if (RequestPair.Key.Key == Directory)
{
// There should be no NULL entries in the map
check(RequestPair.Value);
if (RequestPair.Value->RemoveDelegate(InHandle))
{
if (!RequestPair.Value->HasDelegates())
{
// Remove from the active map and add to the pending delete list
RequestMap.Remove(RequestPair.Key);
RequestsPendingDelete.AddUnique(RequestPair.Value);
// Signal to end the watch which will mark this request for deletion
RequestPair.Value->EndWatchRequest();
}
return true;
}
}
}
return false;
}
void FDirectoryWatcherWindows::Tick( float DeltaSeconds )
{
TRACE_CPUPROFILER_EVENT_SCOPE(FDirectoryWatcherWindows::Tick);
// Iterate over all requests in the request map. Move any that have shutdown into the pending delete
// list. If we find any that are not shutdown, poll for notifications (which call our callbacks) from the
// OS before we process the callbacks we received.
bool bHasPolled = false;
for (TMap<FDirectoryWithFlags, FDirectoryWatchRequestWindows*>::TIterator RequestIt(RequestMap);
RequestIt; ++RequestIt)
{
if ( RequestIt.Value()->IsPendingDelete() )
{
RequestsPendingDelete.AddUnique(RequestIt.Value());
RequestIt.RemoveCurrent();
}
else
{
if (!bHasPolled)
{
// Trigger any OS notifications that are queued up in Windows's MessageQueue. To do this we need to put the
// current thread into an alertable state, which we can do by calling SleepEx(0).
SleepEx(0 /* TimeoutMs. 0 means poll notifies and yield to waiting threads but otherwise return immediately */,
1 /* bAlertable: the notifications will be processed */);
bHasPolled = true;
}
// Pass on the unreal notifications for the request if it received an OS notification.
RequestIt.Value()->ProcessPendingNotifications();
}
}
// Delete any stale or invalid requests, either the ones that we found during tick or ones that were
// queued for deletion in between ticks.
for (TArray<FDirectoryWatchRequestWindows*>::TIterator Iter(RequestsPendingDelete); Iter; ++Iter)
{
FDirectoryWatchRequestWindows* Request = *Iter;
if (Request->IsPendingDelete())
{
// This request is safe to delete. Delete and remove it from the list
delete Request;
NumRequests--;
Iter.RemoveCurrentSwap();
}
}
}