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
|
#pragma once
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "core/hle/kernel/k_hardware_timer_base.h"
|
#include "core/hle/kernel/k_hardware_timer_base.h"
|
||||||
|
#include "yuzu_common/wall_clock.h"
|
||||||
|
|
||||||
namespace Core::Timing {
|
namespace Core::Timing {
|
||||||
struct EventType;
|
struct EventType;
|
||||||
|
|
@ -13,6 +16,12 @@ namespace Kernel {
|
||||||
|
|
||||||
class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
|
class KHardwareTimer : /* public KInterruptTask, */ public KHardwareTimerBase {
|
||||||
public:
|
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} {}
|
explicit KHardwareTimer(KernelCore& kernel) : KHardwareTimerBase{kernel} {}
|
||||||
|
|
||||||
// Public API.
|
// Public API.
|
||||||
|
|
@ -21,6 +30,27 @@ public:
|
||||||
|
|
||||||
s64 GetTick() const;
|
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) {
|
void RegisterAbsoluteTask(KTimerTask* task, s64 task_time) {
|
||||||
KScopedDisableDispatch dd{m_kernel};
|
KScopedDisableDispatch dd{m_kernel};
|
||||||
KScopedSpinLock lk{this->GetLock()};
|
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(Common::IsAligned(address, sizeof(s32)), ResultInvalidAddress);
|
||||||
R_UNLESS(IsValidArbitrationType(arb_type), ResultInvalidEnumValue);
|
R_UNLESS(IsValidArbitrationType(arb_type), ResultInvalidEnumValue);
|
||||||
|
|
||||||
// Convert timeout from nanoseconds to ticks.
|
const s64 timeout = system.Kernel().HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
R_RETURN(
|
R_RETURN(
|
||||||
GetCurrentProcess(system.Kernel()).WaitAddressArbiter(address, arb_type, value, timeout));
|
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(!IsKernelAddress(address), ResultInvalidCurrentMemory);
|
||||||
R_UNLESS(Common::IsAligned(address, sizeof(s32)), ResultInvalidAddress);
|
R_UNLESS(Common::IsAligned(address, sizeof(s32)), ResultInvalidAddress);
|
||||||
|
|
||||||
// Convert timeout from nanoseconds to ticks.
|
const s64 timeout = system.Kernel().HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait on the condition variable.
|
// Wait on the condition variable.
|
||||||
R_RETURN(
|
R_RETURN(
|
||||||
|
|
|
||||||
|
|
@ -53,22 +53,7 @@ Result ReplyAndReceiveImpl(KernelCore& kernel, int32_t* out_index, uintptr_t mes
|
||||||
|
|
||||||
// Receive a message.
|
// Receive a message.
|
||||||
{
|
{
|
||||||
// Convert the timeout from nanoseconds to ticks.
|
const s64 timeout = kernel.HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for a message.
|
// Wait for a message.
|
||||||
while (true) {
|
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.
|
const s64 timeout = kernel.HardwareTimer().GetTimeoutDeadlineNs(timeout_ns);
|
||||||
s64 timeout;
|
|
||||||
if (timeout_ns > 0) {
|
|
||||||
u64 ticks = kernel.HardwareTimer().GetTick();
|
|
||||||
ticks += timeout_ns;
|
|
||||||
ticks += 2;
|
|
||||||
|
|
||||||
timeout = ticks;
|
|
||||||
} else {
|
|
||||||
timeout = timeout_ns;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait on the objects.
|
// Wait on the objects.
|
||||||
Result res = KSynchronizationObject::Wait(kernel, out_index, objs.data(), num_handles, timeout);
|
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.
|
// When the input tick is positive, sleep.
|
||||||
if (ns > 0) {
|
if (ns > 0) {
|
||||||
// Convert the timeout from nanoseconds to ticks.
|
const s64 timeout = kernel.HardwareTimer().GetTimeoutDeadlineNs(ns);
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sleep.
|
// Sleep.
|
||||||
// NOTE: Nintendo does not check the result of this sleep.
|
// NOTE: Nintendo does not check the result of this sleep.
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue