InterruptableConditionVariable: Implement for mingw

This commit is contained in:
Ryan Houdek 2023-04-22 09:31:12 -07:00
parent c9e7d9f331
commit 90f347839d

View File

@ -4,8 +4,14 @@
#include <chrono>
#include <climits>
#include <cstdint>
#ifndef _WIN32
#include <linux/futex.h>
#include <sys/syscall.h>
#else
#include <errhandlingapi.h>
#include <synchapi.h>
#include <winerror.h>
#endif
#include <unistd.h>
namespace FEXCore {
@ -17,6 +23,7 @@ namespace FEXCore {
* call can leave the condition variable in an invalid state that breaks later
* uses of that object and may cause hangs as a consequence.
*/
#ifndef _WIN32
class InterruptableConditionVariable final {
public:
bool Wait(struct timespec *Timeout = nullptr) {
@ -86,4 +93,68 @@ namespace FEXCore {
}
}
};
#else
class InterruptableConditionVariable final {
public:
bool Wait(struct timespec *Timeout = nullptr) {
while (true) {
uint32_t Expected = SIGNALED;
uint32_t Desired = UNSIGNALED;
// If the mutex was already signaled then we can early exit
if (Mutex.compare_exchange_strong(Expected, Desired)) {
return true;
}
// Windows only supports millisecond granularity.
const uint32_t TimeoutMS = Timeout ? Timeout->tv_sec * 1000 + (Timeout->tv_nsec / 1000000) : 0;
// WaitOnAddress returns when the value at `Address` differs from the value at `CompareAddress`.
bool Result = WaitOnAddress(&Mutex, &Desired, 4, TimeoutMS);
if (Timeout && Result == false && GetLastError() == ERROR_TIMEOUT) {
return false;
}
}
}
template<class Rep, class Period>
bool WaitFor(std::chrono::duration<Rep, Period> const& time) {
struct timespec Timeout{};
auto SecondsDuration = std::chrono::duration_cast<std::chrono::seconds>(time);
Timeout.tv_sec = SecondsDuration.count();
Timeout.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(time - SecondsDuration).count();
return Wait(&Timeout);
}
void NotifyOne() {
DoNotify(false);
}
void NotifyAll() {
// Maximum number of waiters
DoNotify(true);
}
private:
std::atomic<uint32_t> Mutex{};
constexpr static uint32_t SIGNALED = 1;
constexpr static uint32_t UNSIGNALED = 0;
void DoNotify(bool All) {
uint32_t Expected = UNSIGNALED;
uint32_t Desired = SIGNALED;
// If the mutex was in an unsignaled state then signal
if (Mutex.compare_exchange_strong(Expected, Desired)) {
if (All) {
WakeByAddressAll(&Mutex);
}
else {
WakeByAddressSingle(&Mutex);
}
}
}
};
#endif
}