// Copyright Epic Games, Inc. All Rights Reserved. #include #include #include #include #if ZEN_PLATFORM_WINDOWS # include #elif ZEN_PLATFORM_LINUX # include #endif namespace zen { #if ZEN_PLATFORM_WINDOWS // The information on how to set the thread name comes from // a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx const DWORD kVCThreadNameException = 0x406D1388; typedef struct tagTHREADNAME_INFO { DWORD dwType; // Must be 0x1000. LPCSTR szName; // Pointer to name (in user addr space). DWORD dwThreadID; // Thread ID (-1=caller thread). DWORD dwFlags; // Reserved for future use, must be zero. } THREADNAME_INFO; // The SetThreadDescription API was brought in version 1607 of Windows 10. typedef HRESULT(WINAPI* SetThreadDescription)(HANDLE hThread, PCWSTR lpThreadDescription); // This function has try handling, so it is separated out of its caller. void SetNameInternal(DWORD thread_id, const char* name) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = name; info.dwThreadID = thread_id; info.dwFlags = 0; __try { RaiseException(kVCThreadNameException, 0, sizeof(info) / sizeof(DWORD), reinterpret_cast(&info)); } __except (EXCEPTION_CONTINUE_EXECUTION) { } } #endif void SetCurrentThreadName([[maybe_unused]] std::string_view ThreadName) { #if ZEN_PLATFORM_WINDOWS // The SetThreadDescription API works even if no debugger is attached. static auto SetThreadDescriptionFunc = reinterpret_cast(::GetProcAddress(::GetModuleHandle(L"Kernel32.dll"), "SetThreadDescription")); if (SetThreadDescriptionFunc) { SetThreadDescriptionFunc(::GetCurrentThread(), Utf8ToWide(ThreadName).c_str()); } // The debugger needs to be around to catch the name in the exception. If // there isn't a debugger, we are just needlessly throwing an exception. if (!::IsDebuggerPresent()) return; std::string ThreadNameZ{ThreadName}; SetNameInternal(GetCurrentThreadId(), ThreadNameZ.c_str()); #else #endif } // namespace zen void RwLock::AcquireShared() { m_Mutex.lock_shared(); } void RwLock::ReleaseShared() { m_Mutex.unlock_shared(); } void RwLock::AcquireExclusive() { m_Mutex.lock(); } void RwLock::ReleaseExclusive() { m_Mutex.unlock(); } ////////////////////////////////////////////////////////////////////////// #if ZEN_PLATFORM_WINDOWS Event::Event() { m_EventHandle = CreateEvent(nullptr, true, false, nullptr); } Event::~Event() { CloseHandle(m_EventHandle); } void Event::Set() { SetEvent(m_EventHandle); } void Event::Reset() { ResetEvent(m_EventHandle); } void Event::Close() { CloseHandle(m_EventHandle); m_EventHandle = nullptr; } bool Event::Wait(int TimeoutMs) { using namespace std::literals; const DWORD Timeout = (TimeoutMs < 0) ? INFINITE : TimeoutMs; DWORD Result = WaitForSingleObject(m_EventHandle, Timeout); if (Result == WAIT_FAILED) { zen::ThrowLastError("Event wait failed"sv); } return (Result == WAIT_OBJECT_0); } ////////////////////////////////////////////////////////////////////////// NamedEvent::NamedEvent(std::u8string_view EventName) : Event(nullptr) { using namespace std::literals; ExtendableStringBuilder<64> Name; Name << "Local\\"sv; Name << EventName; m_EventHandle = CreateEventA(nullptr, true, false, Name.c_str()); } NamedEvent::NamedEvent(std::string_view EventName) : Event(nullptr) { using namespace std::literals; ExtendableStringBuilder<64> Name; Name << "Local\\"sv; Name << EventName; m_EventHandle = CreateEventA(nullptr, true, false, Name.c_str()); } NamedMutex::~NamedMutex() { if (m_MutexHandle) { CloseHandle(m_MutexHandle); } } bool NamedMutex::Create(std::string_view MutexName) { ZEN_ASSERT(m_MutexHandle == nullptr); using namespace std::literals; ExtendableStringBuilder<64> Name; Name << "Global\\"sv; Name << MutexName; m_MutexHandle = CreateMutexA(nullptr, /* InitialOwner */ TRUE, Name.c_str()); return !!m_MutexHandle; } bool NamedMutex::Exists(std::string_view MutexName) { using namespace std::literals; ExtendableStringBuilder<64> Name; Name << "Global\\"sv; Name << MutexName; void* MutexHandle = OpenMutexA(SYNCHRONIZE, /* InheritHandle */ FALSE, Name.c_str()); if (MutexHandle == nullptr) { return false; } CloseHandle(MutexHandle); return true; } #endif // ZEN_PLATFORM_WINDOWS #if ZEN_PLATFORM_WINDOWS ////////////////////////////////////////////////////////////////////////// ProcessHandle::ProcessHandle() = default; void ProcessHandle::Initialize(void* ProcessHandle) { ZEN_ASSERT(m_ProcessHandle == nullptr); // TODO: perform some debug verification here to verify it's a valid handle? m_ProcessHandle = ProcessHandle; m_Pid = GetProcessId(m_ProcessHandle); } ProcessHandle::~ProcessHandle() { Reset(); } void ProcessHandle::Initialize(int Pid) { ZEN_ASSERT(m_ProcessHandle == nullptr); m_ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Pid); using namespace fmt::literals; if (!m_ProcessHandle) { ThrowLastError("ProcessHandle::Initialize(pid: {}) failed"_format(Pid)); } m_Pid = Pid; } bool ProcessHandle::IsRunning() const { DWORD ExitCode = 0; GetExitCodeProcess(m_ProcessHandle, &ExitCode); return ExitCode == STILL_ACTIVE; } bool ProcessHandle::IsValid() const { return (m_ProcessHandle != nullptr) && (m_ProcessHandle != INVALID_HANDLE_VALUE); } void ProcessHandle::Terminate(int ExitCode) { if (IsRunning()) { TerminateProcess(m_ProcessHandle, ExitCode); } DWORD WaitResult = WaitForSingleObject(m_ProcessHandle, INFINITE); if (WaitResult != WAIT_OBJECT_0) { // What might go wrong here, and what is meaningful to act on? } } void ProcessHandle::Reset() { if (IsValid()) { CloseHandle(m_ProcessHandle); m_ProcessHandle = nullptr; } } bool ProcessHandle::Wait(int TimeoutMs) { const DWORD Timeout = (TimeoutMs < 0) ? INFINITE : TimeoutMs; const DWORD WaitResult = WaitForSingleObject(m_ProcessHandle, Timeout); switch (WaitResult) { case WAIT_OBJECT_0: return true; case WAIT_TIMEOUT: return false; case WAIT_FAILED: // What might go wrong here, and what is meaningful to act on? using namespace std::literals; ThrowLastError("Process::Wait failed"sv); } return false; } #endif // ZEN_PLATFORM_WINDOWS ////////////////////////////////////////////////////////////////////////// #if ZEN_PLATFORM_WINDOWS ProcessMonitor::ProcessMonitor() { } ProcessMonitor::~ProcessMonitor() { RwLock::ExclusiveLockScope _(m_Lock); for (HANDLE& Proc : m_ProcessHandles) { CloseHandle(Proc); Proc = 0; } } bool ProcessMonitor::IsRunning() { RwLock::ExclusiveLockScope _(m_Lock); bool FoundOne = false; for (HANDLE& Proc : m_ProcessHandles) { DWORD ExitCode = 0; GetExitCodeProcess(Proc, &ExitCode); if (ExitCode != STILL_ACTIVE) { CloseHandle(Proc); Proc = 0; } else { // Still alive FoundOne = true; } } std::erase_if(m_ProcessHandles, [](HANDLE Handle) { return Handle == 0; }); return FoundOne; } void ProcessMonitor::AddPid(int Pid) { HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Pid); if (ProcessHandle) { RwLock::ExclusiveLockScope _(m_Lock); m_ProcessHandles.push_back(ProcessHandle); } } bool ProcessMonitor::IsActive() const { RwLock::SharedLockScope _(m_Lock); return m_ProcessHandles.empty() == false; } #endif // ZEN_PLATFORM_WINDOWS ////////////////////////////////////////////////////////////////////////// bool IsProcessRunning(int pid) { // This function is arguably not super useful, a pid can be re-used // by the OS so holding on to a pid and polling it over some time // period will not necessarily tell you what you probably want to know. #if ZEN_PLATFORM_WINDOWS HANDLE hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid); if (!hProc) { DWORD Error = zen::GetLastError(); if (Error == ERROR_INVALID_PARAMETER) { return false; } using namespace fmt::literals; ThrowSystemError(Error, "failed to open process with pid {}"_format(pid)); } CloseHandle(hProc); return true; #else ZEN_NOT_IMPLEMENTED(); #endif } int GetCurrentProcessId() { #if ZEN_PLATFORM_WINDOWS return ::GetCurrentProcessId(); #else return getpid(); #endif } void Sleep(int ms) { #if ZEN_PLATFORM_WINDOWS ::Sleep(ms); #else usleep(ms * 1000U); #endif } ////////////////////////////////////////////////////////////////////////// // // Testing related code follows... // void thread_forcelink() { } } // namespace zen