70 lines
1.9 KiB
C++
70 lines
1.9 KiB
C++
/*
|
|
* InputEvents.cpp
|
|
* Shared input activity event source
|
|
* Copyright (c) 2026 Daniel Hammer
|
|
*/
|
|
|
|
#include "InputEvents.hpp"
|
|
#include <atomic>
|
|
#include <Sched/Scheduler.hpp>
|
|
#include <Timekeeping/ApicTimer.hpp>
|
|
|
|
namespace Drivers::InputEvents {
|
|
|
|
static std::atomic<uint64_t> g_serial{1};
|
|
static uint8_t g_waitObject;
|
|
|
|
struct WaitContext {
|
|
uint64_t observedSerial;
|
|
};
|
|
|
|
static bool SerialStillObserved(void* rawContext) {
|
|
auto* context = static_cast<WaitContext*>(rawContext);
|
|
return context != nullptr &&
|
|
g_serial.load(std::memory_order_acquire) == context->observedSerial;
|
|
}
|
|
|
|
uint64_t GetSerial() {
|
|
return g_serial.load(std::memory_order_acquire);
|
|
}
|
|
|
|
void NotifyActivity() {
|
|
g_serial.fetch_add(1, std::memory_order_release);
|
|
Sched::WakeObjectWaiters(&g_waitObject);
|
|
}
|
|
|
|
uint64_t WaitForChange(uint64_t observedSerial, uint64_t timeoutMs) {
|
|
uint64_t current = GetSerial();
|
|
if (current != observedSerial || timeoutMs == 0) {
|
|
return current;
|
|
}
|
|
|
|
WaitContext context{observedSerial};
|
|
uint64_t start = Timekeeping::GetMilliseconds();
|
|
|
|
for (;;) {
|
|
current = GetSerial();
|
|
if (current != observedSerial) {
|
|
return current;
|
|
}
|
|
|
|
uint64_t waitMs = 0;
|
|
if (timeoutMs == UINT64_MAX) {
|
|
waitMs = 0;
|
|
} else {
|
|
uint64_t elapsed = Timekeeping::GetMilliseconds() - start;
|
|
if (elapsed >= timeoutMs) {
|
|
return current;
|
|
}
|
|
waitMs = timeoutMs - elapsed;
|
|
if (waitMs == 0) {
|
|
waitMs = 1;
|
|
}
|
|
}
|
|
|
|
Sched::BlockOnObjectIf(&g_waitObject, waitMs, SerialStillObserved, &context);
|
|
}
|
|
}
|
|
|
|
}
|