diff options
| author | Dan Engelbrecht <[email protected]> | 2023-08-17 22:52:28 +0200 |
|---|---|---|
| committer | GitHub <[email protected]> | 2023-08-17 22:52:28 +0200 |
| commit | dc320d6bfe81f149e8276caabcd165a931711ae7 (patch) | |
| tree | 4fd245d817876f15e90eb30f6577d4550c76f8fc /src | |
| parent | single thread async cache log (#361) (diff) | |
| download | zen-dc320d6bfe81f149e8276caabcd165a931711ae7.tar.xz zen-dc320d6bfe81f149e8276caabcd165a931711ae7.zip | |
Cache process handles for FormatPackageMessage (#360)
Diffstat (limited to 'src')
| -rw-r--r-- | src/zenhttp/httpserver.cpp | 1 | ||||
| -rw-r--r-- | src/zenhttp/httpshared.cpp | 268 | ||||
| -rw-r--r-- | src/zenhttp/include/zenhttp/httpshared.h | 8 | ||||
| -rw-r--r-- | src/zenserver/cache/httpstructuredcache.cpp | 19 | ||||
| -rw-r--r-- | src/zenserver/cache/httpstructuredcache.h | 2 | ||||
| -rw-r--r-- | src/zenutil/include/zenutil/openprocesscache.h | 39 | ||||
| -rw-r--r-- | src/zenutil/openprocesscache.cpp | 150 |
7 files changed, 350 insertions, 137 deletions
diff --git a/src/zenhttp/httpserver.cpp b/src/zenhttp/httpserver.cpp index dbf284ab5..5312e80a2 100644 --- a/src/zenhttp/httpserver.cpp +++ b/src/zenhttp/httpserver.cpp @@ -498,6 +498,7 @@ HttpServerRequest::WriteResponse(HttpResponseCode ResponseCode, HttpContentType std::span<const SharedBuffer> Segments = Payload.GetSegments(); std::vector<IoBuffer> Buffers; + Buffers.reserve(Segments.size()); for (auto& Segment : Segments) { diff --git a/src/zenhttp/httpshared.cpp b/src/zenhttp/httpshared.cpp index 7aade56d2..4a67b69e7 100644 --- a/src/zenhttp/httpshared.cpp +++ b/src/zenhttp/httpshared.cpp @@ -14,6 +14,7 @@ #include <zencore/stream.h> #include <zencore/testing.h> #include <zencore/testutils.h> +#include <zencore/trace.h> #include <span> #include <vector> @@ -27,20 +28,20 @@ namespace zen { const std::string_view HandlePrefix(":?#:"); std::vector<IoBuffer> -FormatPackageMessage(const CbPackage& Data, int TargetProcessPid) +FormatPackageMessage(const CbPackage& Data, void* TargetProcessHandle) { - return FormatPackageMessage(Data, FormatFlags::kDefault, TargetProcessPid); + return FormatPackageMessage(Data, FormatFlags::kDefault, TargetProcessHandle); } CompositeBuffer -FormatPackageMessageBuffer(const CbPackage& Data, int TargetProcessPid) +FormatPackageMessageBuffer(const CbPackage& Data, void* TargetProcessHandle) { - return FormatPackageMessageBuffer(Data, FormatFlags::kDefault, TargetProcessPid); + return FormatPackageMessageBuffer(Data, FormatFlags::kDefault, TargetProcessHandle); } CompositeBuffer -FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid) +FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle) { - std::vector<IoBuffer> Message = FormatPackageMessage(Data, Flags, TargetProcessPid); + std::vector<IoBuffer> Message = FormatPackageMessage(Data, Flags, TargetProcessHandle); std::vector<SharedBuffer> Buffers; @@ -52,43 +53,125 @@ FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, int TargetP return CompositeBuffer(std::move(Buffers)); } -std::vector<IoBuffer> -FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid) +static void +MarshalLocal(CbAttachmentEntry*& AttachmentInfo, + const std::string& Path8, + CbAttachmentReferenceHeader& LocalRef, + const IoHash& AttachmentHash, + bool IsCompressed, + std::vector<IoBuffer>& ResponseBuffers) { - void* TargetProcessHandle = nullptr; -#if ZEN_PLATFORM_WINDOWS - std::vector<HANDLE> DuplicatedHandles; - auto _ = MakeGuard([&DuplicatedHandles, &TargetProcessHandle]() { - if (TargetProcessHandle == nullptr) - { - return; - } - - for (HANDLE DuplicatedHandle : DuplicatedHandles) - { - HANDLE ClosingHandle; - if (::DuplicateHandle((HANDLE)TargetProcessHandle, - DuplicatedHandle, - GetCurrentProcess(), - &ClosingHandle, - 0, - FALSE, - DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) == TRUE) - { - ::CloseHandle(ClosingHandle); - } - } - ::CloseHandle((HANDLE)TargetProcessHandle); - TargetProcessHandle = nullptr; - }); - - if (EnumHasAllFlags(Flags, FormatFlags::kAllowLocalReferences) && TargetProcessPid != 0) + IoBuffer RefBuffer(sizeof(CbAttachmentReferenceHeader) + Path8.size()); + + CbAttachmentReferenceHeader* RefHdr = RefBuffer.MutableData<CbAttachmentReferenceHeader>(); + *RefHdr++ = LocalRef; + memcpy(RefHdr, Path8.data(), Path8.size()); + + *AttachmentInfo++ = {.PayloadSize = RefBuffer.GetSize(), + .Flags = (IsCompressed ? uint32_t(CbAttachmentEntry::kIsCompressed) : 0u) | CbAttachmentEntry::kIsLocalRef, + .AttachmentHash = AttachmentHash}; + + ResponseBuffers.push_back(std::move(RefBuffer)); +}; + +static bool +IsLocalRef(tsl::robin_map<void*, std::string>& FileNameMap, + std::vector<void*>& DuplicatedHandles, + const CompositeBuffer& AttachmentBinary, + bool DenyPartialLocalReferences, + void* TargetProcessHandle, + CbAttachmentReferenceHeader& LocalRef, + std::string& Path8) +{ + const SharedBuffer& Segment = AttachmentBinary.GetSegments().front(); + IoBufferFileReference Ref; + const IoBuffer& SegmentBuffer = Segment.AsIoBuffer(); + + if (!SegmentBuffer.GetFileReference(Ref)) + { + return false; + } + + if (DenyPartialLocalReferences && !SegmentBuffer.IsWholeFile()) + { + return false; + } + + if (auto It = FileNameMap.find(Ref.FileHandle); It != FileNameMap.end()) { - TargetProcessHandle = OpenProcess(PROCESS_DUP_HANDLE, FALSE, TargetProcessPid); + Path8 = It->second; } -#else - ZEN_UNUSED(TargetProcessPid); - void* DuplicatedHandles = nullptr; + else + { + bool UseFilePath = true; +#if ZEN_PLATFORM_WINDOWS + if (TargetProcessHandle != nullptr) + { + HANDLE TargetHandle = INVALID_HANDLE_VALUE; + BOOL OK = ::DuplicateHandle(GetCurrentProcess(), + Ref.FileHandle, + (HANDLE)TargetProcessHandle, + &TargetHandle, + FILE_GENERIC_READ, + FALSE, + 0); + if (OK) + { + DuplicatedHandles.push_back((void*)TargetHandle); + Path8 = fmt::format("{}{}", HandlePrefix, reinterpret_cast<uint64_t>(TargetHandle)); + UseFilePath = false; + } + } +#else // ZEN_PLATFORM_WINDOWS + ZEN_UNUSED(TargetProcessHandle); + ZEN_UNUSED(DuplicatedHandles); + // Not supported on Linux/Mac. Could potentially use pidfd_getfd() but that requires a fairly new Linux kernel/includes and to + // deal with acceess rights etc. +#endif // ZEN_PLATFORM_WINDOWS + if (UseFilePath) + { + ExtendablePathBuilder<256> LocalRefFile; + LocalRefFile.Append(std::filesystem::absolute(PathFromHandle(Ref.FileHandle))); + Path8 = LocalRefFile.ToUtf8(); + } + FileNameMap.insert_or_assign(Ref.FileHandle, Path8); + } + + LocalRef.AbsolutePathLength = gsl::narrow<uint16_t>(Path8.size()); + LocalRef.PayloadByteOffset = Ref.FileChunkOffset; + LocalRef.PayloadByteSize = Ref.FileChunkSize; + + return true; +}; + +std::vector<IoBuffer> +FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle) +{ + ZEN_TRACE_CPU("FormatPackageMessage"); + + std::vector<void*> DuplicatedHandles; +#if ZEN_PLATFORM_WINDOWS + auto _ = MakeGuard([&DuplicatedHandles, &TargetProcessHandle]() { + if (TargetProcessHandle == nullptr) + { + return; + } + + for (void* DuplicatedHandle : DuplicatedHandles) + { + HANDLE ClosingHandle; + if (::DuplicateHandle((HANDLE)TargetProcessHandle, + (HANDLE)DuplicatedHandle, + GetCurrentProcess(), + &ClosingHandle, + 0, + FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) == TRUE) + { + ::CloseHandle(ClosingHandle); + } + } + }); #endif // ZEN_PLATFORM_WINDOWS const std::span<const CbAttachment>& Attachments = Data.GetAttachments(); @@ -118,91 +201,8 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcess *AttachmentInfo++ = {.PayloadSize = RootIoBuffer.Size(), .Flags = CbAttachmentEntry::kIsObject, .AttachmentHash = Data.GetObjectHash()}; // Attachment payloads - - auto MarshalLocal = [&AttachmentInfo, &ResponseBuffers](const std::string& Path8, - CbAttachmentReferenceHeader& LocalRef, - const IoHash& AttachmentHash, - bool IsCompressed) { - IoBuffer RefBuffer(sizeof(CbAttachmentReferenceHeader) + Path8.size()); - - CbAttachmentReferenceHeader* RefHdr = RefBuffer.MutableData<CbAttachmentReferenceHeader>(); - *RefHdr++ = LocalRef; - memcpy(RefHdr, Path8.data(), Path8.size()); - - *AttachmentInfo++ = {.PayloadSize = RefBuffer.GetSize(), - .Flags = (IsCompressed ? uint32_t(CbAttachmentEntry::kIsCompressed) : 0u) | CbAttachmentEntry::kIsLocalRef, - .AttachmentHash = AttachmentHash}; - - ResponseBuffers.push_back(std::move(RefBuffer)); - }; - tsl::robin_map<void*, std::string> FileNameMap; - auto IsLocalRef = [&FileNameMap, &DuplicatedHandles](const CompositeBuffer& AttachmentBinary, - bool DenyPartialLocalReferences, - void* TargetProcessHandle, - CbAttachmentReferenceHeader& LocalRef, - std::string& Path8) -> bool { - const SharedBuffer& Segment = AttachmentBinary.GetSegments().front(); - IoBufferFileReference Ref; - const IoBuffer& SegmentBuffer = Segment.AsIoBuffer(); - - if (!SegmentBuffer.GetFileReference(Ref)) - { - return false; - } - - if (DenyPartialLocalReferences && !SegmentBuffer.IsWholeFile()) - { - return false; - } - - if (auto It = FileNameMap.find(Ref.FileHandle); It != FileNameMap.end()) - { - Path8 = It->second; - } - else - { - bool UseFilePath = true; -#if ZEN_PLATFORM_WINDOWS - if (TargetProcessHandle != nullptr) - { - HANDLE TargetHandle = INVALID_HANDLE_VALUE; - BOOL OK = ::DuplicateHandle(GetCurrentProcess(), - Ref.FileHandle, - (HANDLE)TargetProcessHandle, - &TargetHandle, - FILE_GENERIC_READ, - FALSE, - 0); - if (OK) - { - DuplicatedHandles.push_back(TargetHandle); - Path8 = fmt::format("{}{}", HandlePrefix, reinterpret_cast<uint64_t>(TargetHandle)); - UseFilePath = false; - } - } -#else // ZEN_PLATFORM_WINDOWS - ZEN_UNUSED(TargetProcessHandle); - // Not supported on Linux/Mac. Could potentially use pidfd_getfd() but that requires a fairly new Linux kernel/includes and to - // deal with acceess rights etc. -#endif // ZEN_PLATFORM_WINDOWS - if (UseFilePath) - { - ExtendablePathBuilder<256> LocalRefFile; - LocalRefFile.Append(std::filesystem::absolute(PathFromHandle(Ref.FileHandle))); - Path8 = LocalRefFile.ToUtf8(); - } - FileNameMap.insert_or_assign(Ref.FileHandle, Path8); - } - - LocalRef.AbsolutePathLength = gsl::narrow<uint16_t>(Path8.size()); - LocalRef.PayloadByteOffset = Ref.FileChunkOffset; - LocalRef.PayloadByteSize = Ref.FileChunkSize; - - return true; - }; - for (const CbAttachment& Attachment : Attachments) { if (Attachment.IsNull()) @@ -226,7 +226,13 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcess if (MarshalByLocalRef) { - MarshalByLocalRef = IsLocalRef(Compressed, DenyPartialLocalReferences, TargetProcessHandle, LocalRef, Path8); + MarshalByLocalRef = IsLocalRef(FileNameMap, + DuplicatedHandles, + Compressed, + DenyPartialLocalReferences, + TargetProcessHandle, + LocalRef, + Path8); } if (MarshalByLocalRef) @@ -236,7 +242,7 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcess #if ZEN_PLATFORM_WINDOWS IsHandle = Path8.starts_with(HandlePrefix); #endif - MarshalLocal(Path8, LocalRef, AttachmentHash, IsCompressed); + MarshalLocal(AttachmentInfo, Path8, LocalRef, AttachmentHash, IsCompressed, ResponseBuffers); ZEN_DEBUG("Marshalled '{}' as file {} of {} bytes", Path8, IsHandle ? "handle" : "path", Compressed.GetSize()); } else @@ -272,7 +278,13 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcess if (MarshalByLocalRef) { - MarshalByLocalRef = IsLocalRef(AttachmentBinary, DenyPartialLocalReferences, TargetProcessHandle, LocalRef, Path8); + MarshalByLocalRef = IsLocalRef(FileNameMap, + DuplicatedHandles, + AttachmentBinary, + DenyPartialLocalReferences, + TargetProcessHandle, + LocalRef, + Path8); } if (MarshalByLocalRef) @@ -282,7 +294,7 @@ FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcess #if ZEN_PLATFORM_WINDOWS IsHandle = Path8.starts_with(HandlePrefix); #endif - MarshalLocal(Path8, LocalRef, AttachmentHash, IsCompressed); + MarshalLocal(AttachmentInfo, Path8, LocalRef, AttachmentHash, IsCompressed, ResponseBuffers); ZEN_DEBUG("Marshalled '{}' as file {} of {} bytes", Path8, IsHandle ? "handle" : "path", AttachmentBinary.GetSize()); } else @@ -332,6 +344,8 @@ IsPackageMessage(IoBuffer Payload) CbPackage ParsePackageMessage(IoBuffer Payload, std::function<IoBuffer(const IoHash&, uint64_t)> CreateBuffer) { + ZEN_TRACE_CPU("ParsePackageMessage"); + if (!Payload) { return {}; diff --git a/src/zenhttp/include/zenhttp/httpshared.h b/src/zenhttp/include/zenhttp/httpshared.h index d335572c5..cf74b6b21 100644 --- a/src/zenhttp/include/zenhttp/httpshared.h +++ b/src/zenhttp/include/zenhttp/httpshared.h @@ -94,8 +94,8 @@ enum class RpcAcceptOptions : uint16_t gsl_DEFINE_ENUM_BITMASK_OPERATORS(RpcAcceptOptions); -std::vector<IoBuffer> FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid = 0); -CompositeBuffer FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, int TargetProcessPid = 0); +std::vector<IoBuffer> FormatPackageMessage(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle = nullptr); +CompositeBuffer FormatPackageMessageBuffer(const CbPackage& Data, FormatFlags Flags, void* TargetProcessHandle = nullptr); CbPackage ParsePackageMessage( IoBuffer Payload, std::function<IoBuffer(const IoHash& Cid, uint64_t Size)> CreateBuffer = [](const IoHash&, uint64_t Size) -> IoBuffer { @@ -105,8 +105,8 @@ bool IsPackageMessage(IoBuffer Payload); bool ParsePackageMessageWithLegacyFallback(const IoBuffer& Response, CbPackage& OutPackage); -std::vector<IoBuffer> FormatPackageMessage(const CbPackage& Data, int TargetProcessPid = 0); -CompositeBuffer FormatPackageMessageBuffer(const CbPackage& Data, int TargetProcessPid = 0); +std::vector<IoBuffer> FormatPackageMessage(const CbPackage& Data, void* TargetProcessHandle = nullptr); +CompositeBuffer FormatPackageMessageBuffer(const CbPackage& Data, void* TargetProcessHandle = nullptr); /** Streaming reader for compact binary packages diff --git a/src/zenserver/cache/httpstructuredcache.cpp b/src/zenserver/cache/httpstructuredcache.cpp index 51d3d94d8..9b7ec61d7 100644 --- a/src/zenserver/cache/httpstructuredcache.cpp +++ b/src/zenserver/cache/httpstructuredcache.cpp @@ -1652,7 +1652,8 @@ HttpStructuredCacheService::ReplayRequestRecorder(const CacheRequestContext& Co { if (AcceptMagic == kCbPkgMagic) { - FormatFlags Flags = FormatFlags::kDefault; + void* TargetProcessHandle = nullptr; + FormatFlags Flags = FormatFlags::kDefault; if (EnumHasAllFlags(AcceptFlags, RpcAcceptOptions::kAllowLocalReferences)) { Flags |= FormatFlags::kAllowLocalReferences; @@ -1660,8 +1661,9 @@ HttpStructuredCacheService::ReplayRequestRecorder(const CacheRequestContext& Co { Flags |= FormatFlags::kDenyPartialLocalReferences; } + TargetProcessHandle = m_OpenProcessCache.GetProcessHandle(Context.SessionId, TargetPid); } - CompositeBuffer RpcResponseBuffer = FormatPackageMessageBuffer(RpcResult, Flags, TargetPid); + CompositeBuffer RpcResponseBuffer = FormatPackageMessageBuffer(RpcResult, Flags, TargetProcessHandle); ZEN_ASSERT(RpcResponseBuffer.GetSize() > 0); } else @@ -1705,6 +1707,7 @@ HttpStructuredCacheService::HandleRpcRequest(HttpServerRequest& Request) Request.WriteResponseAsync( [this, RequestContext, Body = Request.ReadPayload(), ContentType, AcceptType](HttpServerRequest& AsyncRequest) mutable { + ZEN_TRACE_CPU("z$::Http::HandleRpcRequest::WriteResponseAsync"); std::uint64_t RequestIndex = m_RequestRecorder ? m_RequestRecorder->RecordRequest(ContentType, AcceptType, Body) : ~0ull; uint32_t AcceptMagic = 0; @@ -1726,7 +1729,8 @@ HttpStructuredCacheService::HandleRpcRequest(HttpServerRequest& Request) } if (AcceptMagic == kCbPkgMagic) { - FormatFlags Flags = FormatFlags::kDefault; + void* TargetProcessHandle = nullptr; + FormatFlags Flags = FormatFlags::kDefault; if (EnumHasAllFlags(AcceptFlags, RpcAcceptOptions::kAllowLocalReferences)) { Flags |= FormatFlags::kAllowLocalReferences; @@ -1734,8 +1738,9 @@ HttpStructuredCacheService::HandleRpcRequest(HttpServerRequest& Request) { Flags |= FormatFlags::kDenyPartialLocalReferences; } + TargetProcessHandle = m_OpenProcessCache.GetProcessHandle(RequestContext.SessionId, TargetProcessId); } - CompositeBuffer RpcResponseBuffer = FormatPackageMessageBuffer(RpcResult, Flags, TargetProcessId); + CompositeBuffer RpcResponseBuffer = FormatPackageMessageBuffer(RpcResult, Flags, TargetProcessHandle); if (RequestIndex != ~0ull) { ZEN_ASSERT(m_RequestRecorder); @@ -2400,9 +2405,11 @@ HttpStructuredCacheService::HandleRpcGetCacheValues(const CacheRequestContext& C std::vector<size_t> RemoteRequestIndexes; - const bool HasUpstream = m_UpstreamCache.IsActive(); + const bool HasUpstream = m_UpstreamCache.IsActive(); + CbArrayView RequestView = Params["Requests"sv].AsArrayView(); + Requests.reserve(RequestView.Num()); - for (CbFieldView RequestField : Params["Requests"sv]) + for (CbFieldView RequestField : RequestView) { Stopwatch Timer; diff --git a/src/zenserver/cache/httpstructuredcache.h b/src/zenserver/cache/httpstructuredcache.h index 60714d6ae..7ad3f5ac8 100644 --- a/src/zenserver/cache/httpstructuredcache.h +++ b/src/zenserver/cache/httpstructuredcache.h @@ -7,6 +7,7 @@ #include <zenhttp/httpstats.h> #include <zenhttp/httpstatus.h> #include <zenutil/cache/cache.h> +#include <zenutil/openprocesscache.h> #include <memory> #include <vector> @@ -180,6 +181,7 @@ private: metrics::OperationTiming m_UpstreamGetRequestTiming; CacheStats m_CacheStats; const DiskWriteBlocker* m_DiskWriteBlocker = nullptr; + OpenProcessCache m_OpenProcessCache; void ReplayRequestRecorder(const CacheRequestContext& Context, cache::IRpcRequestReplayer& Replayer, uint32_t ThreadCount); diff --git a/src/zenutil/include/zenutil/openprocesscache.h b/src/zenutil/include/zenutil/openprocesscache.h new file mode 100644 index 000000000..3db4e0b42 --- /dev/null +++ b/src/zenutil/include/zenutil/openprocesscache.h @@ -0,0 +1,39 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#pragma once + +#include <zencore/thread.h> +#include <zencore/uid.h> + +#include <thread> +#include <unordered_map> + +namespace zen { + +class OpenProcessCache +{ +public: + OpenProcessCache(); + ~OpenProcessCache(); + + void* GetProcessHandle(Oid SessionId, int ProcessPid); + +private: +#if ZEN_PLATFORM_WINDOWS + struct Process + { + int ProcessPid; + void* ProcessHandle; + }; + + void GCHandles(); + void GcWorker(); + + RwLock m_SessionsLock; + std::unordered_map<Oid, Process, Oid::Hasher> m_Sessions; + std::thread m_GcThread; + Event m_GcExitEvent; +#endif // ZEN_PLATFORM_WINDOWS +}; + +} // namespace zen diff --git a/src/zenutil/openprocesscache.cpp b/src/zenutil/openprocesscache.cpp new file mode 100644 index 000000000..6c8bf3de9 --- /dev/null +++ b/src/zenutil/openprocesscache.cpp @@ -0,0 +1,150 @@ +// Copyright Epic Games, Inc. All Rights Reserved. + +#include "zenutil/openprocesscache.h" +#include <zencore/fmtutils.h> +#include <zencore/logging.h> + +#if ZEN_PLATFORM_WINDOWS +# include <zencore/windows.h> +#else +# include <sys/mman.h> +#endif + +////////////////////////////////////////////////////////////////////////// + +namespace zen { + +OpenProcessCache::OpenProcessCache() +#if ZEN_PLATFORM_WINDOWS +: m_GcThread(&OpenProcessCache::GcWorker, this) +#endif // ZEN_PLATFORM_WINDOWS +{ +} + +OpenProcessCache::~OpenProcessCache() +{ +#if ZEN_PLATFORM_WINDOWS + try + { + m_GcExitEvent.Set(); + if (m_GcThread.joinable()) + { + m_GcThread.join(); + } + RwLock::ExclusiveLockScope Lock(m_SessionsLock); + for (const auto& It : m_Sessions) + { + if (It.second.ProcessHandle == nullptr) + { + continue; + } + CloseHandle(It.second.ProcessHandle); + } + m_Sessions.clear(); + } + catch (std::exception& Ex) + { + ZEN_ERROR("OpenProcessCache destructor failed with reason: `{}`", Ex.what()); + } +#endif // ZEN_PLATFORM_WINDOWS +} + +void* +OpenProcessCache::GetProcessHandle(Oid SessionId, int ProcessPid) +{ + if (ProcessPid == 0) + { + return nullptr; + } +#if ZEN_PLATFORM_WINDOWS + RwLock::ExclusiveLockScope Lock(m_SessionsLock); + + void* DeadHandle = nullptr; + if (auto It = m_Sessions.find(SessionId); It != m_Sessions.end()) + { + if (ProcessPid == It->second.ProcessPid) + { + void* ProcessHandle = It->second.ProcessHandle; + ZEN_ASSERT(ProcessHandle != nullptr); + DWORD ExitCode = 0; + GetExitCodeProcess(It->second.ProcessHandle, &ExitCode); + if (ExitCode == STILL_ACTIVE) + { + return It->second.ProcessHandle; + } + } + + // Remove the existing stale handle + ZEN_INFO("Closing stale target process pid {} for session: {}", It->second.ProcessPid, SessionId, It->second.ProcessHandle); + DeadHandle = It->second.ProcessHandle; + m_Sessions.erase(It); + } + + // Cache new handle + void* ProcessHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_DUP_HANDLE, FALSE, ProcessPid); + ZEN_INFO("Opening target process pid {} for session {}: {}", ProcessPid, SessionId, ProcessHandle == nullptr ? "failed" : "success"); + m_Sessions.insert_or_assign(SessionId, Process{.ProcessPid = ProcessPid, .ProcessHandle = ProcessHandle}); + Lock.ReleaseNow(); + + if (DeadHandle != nullptr) + { + CloseHandle(DeadHandle); + } + + return ProcessHandle; +#else // ZEN_PLATFORM_WINDOWS + ZEN_UNUSED(SessionId); + return nullptr; +#endif // ZEN_PLATFORM_WINDOWS +} + +#if ZEN_PLATFORM_WINDOWS +void +OpenProcessCache::GCHandles() +{ + std::vector<void*> DeadHandles; + RwLock::ExclusiveLockScope Lock(m_SessionsLock); + for (auto& It : m_Sessions) + { + if (It.second.ProcessHandle == nullptr) + { + continue; + } + ZEN_ASSERT(It.second.ProcessPid != 0); + DWORD ExitCode = 0; + GetExitCodeProcess(It.second.ProcessHandle, &ExitCode); + if (ExitCode == STILL_ACTIVE) + { + continue; + } + ZEN_INFO("GC stale target process pid {} for session {}: {}", It.second.ProcessPid, It.first, It.second.ProcessHandle); + DeadHandles.push_back(It.second.ProcessHandle); + It.second.ProcessPid = 0; + It.second.ProcessHandle = nullptr; + } + Lock.ReleaseNow(); + + for (auto Handle : DeadHandles) + { + CloseHandle(Handle); + } +} + +void +OpenProcessCache::GcWorker() +{ + while (!m_GcExitEvent.Wait(500)) + { + try + { + GCHandles(); + } + catch (std::exception& Ex) + { + ZEN_ERROR("gc of open process cache failed with reason: `{}`", Ex.what()); + } + } +} +#endif // ZEN_PLATFORM_WINDOWS + +} // namespace zen |