Preserve the two guest tick margin in SVC timeout deadlines (#338)
This commit is contained in:
parent
4e3a9cbbdb
commit
ba84dd878a
|
|
@ -3,7 +3,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "core/hle/kernel/k_hardware_timer_base.h"
|
||||
#include "yuzu_common/wall_clock.h"
|
||||
|
||||
namespace Core::Timing {
|
||||
struct EventType;
|
||||
|
|
@ -13,6 +16,12 @@ namespace Kernel {
|
|||
|
||||
class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
|
||||
public:
|
||||
// SVC timeouts include two guest timer ticks. Our deadlines remain in
|
||||
// nanoseconds, so round this margin up rather than adding two nanoseconds.
|
||||
static constexpr s64 DefaultTimeIncrementNanoseconds =
|
||||
(2 * 1'000'000'000LL + Common::WallClock::CNTFRQ - 1) / Common::WallClock::CNTFRQ;
|
||||
static_assert(DefaultTimeIncrementNanoseconds == 105);
|
||||
|
||||
explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
|
||||
|
||||
// Public API.
|
||||
|
|
@ -21,6 +30,27 @@ public:
|
|||
|
||||
s64 GetTick() const;
|
||||
|
||||
s64 GetTimeoutDeadlineNs(s64 timeout_ns) const {
|
||||
// Preserve immediate polling and infinite waits without reading the clock.
|
||||
if (timeout_ns <= 0) {
|
||||
return timeout_ns;
|
||||
}
|
||||
|
||||
constexpr s64 max_deadline = (std::numeric_limits<s64>::max)();
|
||||
if (timeout_ns > max_deadline - DefaultTimeIncrementNanoseconds) {
|
||||
return max_deadline;
|
||||
}
|
||||
|
||||
const s64 offset_ns = timeout_ns + DefaultTimeIncrementNanoseconds;
|
||||
const s64 now_ns = GetTick();
|
||||
// The timer is nonnegative. Check the remaining range before adding;
|
||||
// a check after signed overflow would already be undefined behavior.
|
||||
if (static_cast<u64>(now_ns) > static_cast<u64>(max_deadline - offset_ns)) {
|
||||
return max_deadline;
|
||||
}
|
||||
return now_ns + offset_ns;
|
||||
}
|
||||
|
||||
void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
|
||||
KScopedDisableDispatch dd{m_kernel};
|
||||
KScopedSpinLock lk{this->GetLock()};
|
||||
|
|
|
|||
|
|
@ -48,21 +48,7 @@ Result WaitForAddress(Core::System& system, u64 address, ArbitrationType arb_typ
|
|||
R_UNLESS(Common::IsAligned(address, sizeof(s32)), ResultInvalidAddress);
|
||||
R_UNLESS(IsValidArbitrationType(arb_type), ResultInvalidEnumValue);
|
||||
|
||||
// Convert timeout from nanoseconds to ticks.
|
||||
s64 timeout{};
|
||||
if (timeout_ns > 0) {
|
||||
const s64 offset_tick(timeout_ns);
|
||||
if (offset_tick > 0) {
|
||||
timeout = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2;
|
||||
if (timeout <= 0) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
const s64 timeout = system.Kernel().HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||
|
||||
R_RETURN(
|
||||
GetCurrentProcess(system.Kernel()).WaitAddressArbiter(address, arb_type, value, timeout));
|
||||
|
|
|
|||
|
|
@ -21,21 +21,7 @@ Result WaitProcessWideKeyAtomic(Core::System& system, u64 address, u64 cv_key, u
|
|||
R_UNLESS(!IsKernelAddress(address), ResultInvalidCurrentMemory);
|
||||
R_UNLESS(Common::IsAligned(address, sizeof(s32)), ResultInvalidAddress);
|
||||
|
||||
// Convert timeout from nanoseconds to ticks.
|
||||
s64 timeout{};
|
||||
if (timeout_ns > 0) {
|
||||
const s64 offset_tick(timeout_ns);
|
||||
if (offset_tick > 0) {
|
||||
timeout = system.Kernel().HardwareTimer().GetTick() + offset_tick + 2;
|
||||
if (timeout <= 0) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
const s64 timeout = system.Kernel().HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||
|
||||
// Wait on the condition variable.
|
||||
R_RETURN(
|
||||
|
|
|
|||
|
|
@ -53,22 +53,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
|
|||
|
||||
// Receive a message.
|
||||
{
|
||||
// Convert the timeout from nanoseconds to ticks.
|
||||
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
const s64 offset_tick(timeout_ns);
|
||||
if (offset_tick > 0) {
|
||||
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
|
||||
if (timeout <= 0) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
const s64 timeout = kernel.HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||
|
||||
// Wait for a message.
|
||||
while (true) {
|
||||
|
|
|
|||
|
|
@ -84,17 +84,7 @@ Result WaitSynchronization(Core::System& system, int32_t* out_index, u64 user_ha
|
|||
}
|
||||
};
|
||||
|
||||
// Convert the timeout from nanoseconds to ticks.
|
||||
s64 timeout;
|
||||
if (timeout_ns > 0) {
|
||||
u64 ticks = kernel.HardwareTimer().GetTick();
|
||||
ticks += timeout_ns;
|
||||
ticks += 2;
|
||||
|
||||
timeout = ticks;
|
||||
} else {
|
||||
timeout = timeout_ns;
|
||||
}
|
||||
const s64 timeout = kernel.HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||
|
||||
// Wait on the objects.
|
||||
Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout);
|
||||
|
|
|
|||
|
|
@ -111,19 +111,7 @@ void SleepThread(Core::System& system, s64 ns) {
|
|||
|
||||
// When the input tick is positive, sleep.
|
||||
if (ns > 0) {
|
||||
// Convert the timeout from nanoseconds to ticks.
|
||||
// NOTE: Nintendo does not use this conversion logic in WaitSynchronization...
|
||||
s64 timeout;
|
||||
|
||||
const s64 offset_tick(ns);
|
||||
if (offset_tick > 0) {
|
||||
timeout = kernel.HardwareTimer().GetTick() + offset_tick + 2;
|
||||
if (timeout <= 0) {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
} else {
|
||||
timeout = std::numeric_limits<s64>::max();
|
||||
}
|
||||
const s64 timeout = kernel.HardwareTimer().GetTimeoutDeadlineNs(ns);
|
||||
|
||||
// Sleep.
|
||||
// NOTE: Nintendo does not check the result of this sleep.
|
||||
|
|
|
|||
Loading…
Reference in New Issue