early-access version 2824
This commit is contained in:
@@ -139,7 +139,6 @@ struct System::Impl {
|
||||
|
||||
kernel.Suspend(false);
|
||||
core_timing.SyncPause(false);
|
||||
cpu_manager.Pause(false);
|
||||
is_paused = false;
|
||||
|
||||
audio_core->PauseSinks(false);
|
||||
@@ -155,7 +154,6 @@ struct System::Impl {
|
||||
|
||||
core_timing.SyncPause(true);
|
||||
kernel.Suspend(true);
|
||||
cpu_manager.Pause(true);
|
||||
is_paused = true;
|
||||
|
||||
return status;
|
||||
@@ -170,7 +168,6 @@ struct System::Impl {
|
||||
std::unique_lock<std::mutex> lk(suspend_guard);
|
||||
kernel.Suspend(true);
|
||||
core_timing.SyncPause(true);
|
||||
cpu_manager.Pause(true);
|
||||
return lk;
|
||||
}
|
||||
|
||||
@@ -178,7 +175,6 @@ struct System::Impl {
|
||||
if (!is_paused) {
|
||||
core_timing.SyncPause(false);
|
||||
kernel.Suspend(false);
|
||||
cpu_manager.Pause(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -348,13 +344,14 @@ struct System::Impl {
|
||||
gpu_core->NotifyShutdown();
|
||||
}
|
||||
|
||||
kernel.ShutdownCores();
|
||||
cpu_manager.Shutdown();
|
||||
debugger.reset();
|
||||
kernel.CloseServices();
|
||||
services.reset();
|
||||
service_manager.reset();
|
||||
cheat_engine.reset();
|
||||
telemetry_session.reset();
|
||||
cpu_manager.Shutdown();
|
||||
time_manager.Shutdown();
|
||||
core_timing.Shutdown();
|
||||
app_loader.reset();
|
||||
|
||||
@@ -22,11 +22,11 @@ std::shared_ptr<EventType> CreateEvent(std::string name, TimedCallback&& callbac
|
||||
}
|
||||
|
||||
struct CoreTiming::Event {
|
||||
u64 time;
|
||||
s64 time;
|
||||
u64 fifo_order;
|
||||
std::uintptr_t user_data;
|
||||
std::weak_ptr<EventType> type;
|
||||
u64 reschedule_time;
|
||||
s64 reschedule_time;
|
||||
|
||||
// Sort by time, unless the times are the same, in which case sort by
|
||||
// the order added to the queue
|
||||
@@ -59,7 +59,8 @@ void CoreTiming::Initialize(std::function<void()>&& on_thread_init_) {
|
||||
event_fifo_id = 0;
|
||||
shutting_down = false;
|
||||
ticks = 0;
|
||||
const auto empty_timed_callback = [](std::uintptr_t, std::chrono::nanoseconds) {};
|
||||
const auto empty_timed_callback = [](std::uintptr_t, u64, std::chrono::nanoseconds)
|
||||
-> std::optional<std::chrono::nanoseconds> { return std::nullopt; };
|
||||
ev_lost = CreateEvent("_lost_event", empty_timed_callback);
|
||||
if (is_multicore) {
|
||||
worker_threads.emplace_back(ThreadEntry, std::ref(*this), 0);
|
||||
@@ -77,6 +78,7 @@ void CoreTiming::Shutdown() {
|
||||
thread.join();
|
||||
}
|
||||
worker_threads.clear();
|
||||
pause_callbacks.clear();
|
||||
ClearPendingEvents();
|
||||
has_started = false;
|
||||
}
|
||||
@@ -94,6 +96,14 @@ void CoreTiming::Pause(bool is_paused_) {
|
||||
}
|
||||
}
|
||||
paused_state.store(is_paused_, std::memory_order_relaxed);
|
||||
|
||||
if (!is_paused_) {
|
||||
pause_end_time = GetGlobalTimeNs().count();
|
||||
}
|
||||
|
||||
for (auto& cb : pause_callbacks) {
|
||||
cb(is_paused_);
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::SyncPause(bool is_paused_) {
|
||||
@@ -117,6 +127,14 @@ void CoreTiming::SyncPause(bool is_paused_) {
|
||||
wait_signal_cv.wait(main_lock, [this] { return pause_count == 0; });
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_paused_) {
|
||||
pause_end_time = GetGlobalTimeNs().count();
|
||||
}
|
||||
|
||||
for (auto& cb : pause_callbacks) {
|
||||
cb(is_paused_);
|
||||
}
|
||||
}
|
||||
|
||||
bool CoreTiming::IsRunning() const {
|
||||
@@ -130,12 +148,12 @@ bool CoreTiming::HasPendingEvents() const {
|
||||
|
||||
void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data) {
|
||||
std::uintptr_t user_data, bool absolute_time) {
|
||||
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + ns_into_future).count());
|
||||
const auto next_time{absolute_time ? ns_into_future : GetGlobalTimeNs() + ns_into_future};
|
||||
|
||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type, 0});
|
||||
event_queue.emplace_back(Event{next_time.count(), event_fifo_id++, user_data, event_type, 0});
|
||||
pending_events.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
@@ -145,30 +163,15 @@ void CoreTiming::ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleEventAt(std::chrono::nanoseconds next_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data) {
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
const u64 timeout = static_cast<u64>(next_time.count());
|
||||
|
||||
event_queue.emplace_back(Event{timeout, event_fifo_id++, user_data, event_type, 0});
|
||||
pending_events.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
|
||||
if (is_multicore) {
|
||||
event_cv.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds time,
|
||||
void CoreTiming::ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||
std::chrono::nanoseconds resched_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data) {
|
||||
std::uintptr_t user_data, bool absolute_time) {
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
const u64 timeout = static_cast<u64>((GetGlobalTimeNs() + time).count());
|
||||
const auto next_time{absolute_time ? start_time : GetGlobalTimeNs() + start_time};
|
||||
|
||||
event_queue.emplace_back(
|
||||
Event{timeout, event_fifo_id++, user_data, event_type, static_cast<u64>(time.count())});
|
||||
Event{next_time.count(), event_fifo_id++, user_data, event_type, resched_time.count()});
|
||||
pending_events.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
@@ -247,6 +250,11 @@ void CoreTiming::RemoveEvent(const std::shared_ptr<EventType>& event_type) {
|
||||
}
|
||||
}
|
||||
|
||||
void CoreTiming::RegisterPauseCallback(PauseCallback&& callback) {
|
||||
std::unique_lock main_lock(event_mutex);
|
||||
pause_callbacks.emplace_back(std::move(callback));
|
||||
}
|
||||
|
||||
std::optional<s64> CoreTiming::Advance() {
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
|
||||
@@ -257,21 +265,31 @@ std::optional<s64> CoreTiming::Advance() {
|
||||
event_queue.pop_back();
|
||||
|
||||
if (const auto event_type{evt.type.lock()}) {
|
||||
|
||||
event_mutex.unlock();
|
||||
|
||||
const s64 delay = static_cast<s64>(GetGlobalTimeNs().count() - evt.time);
|
||||
event_type->callback(evt.user_data, std::chrono::nanoseconds{delay});
|
||||
const auto new_schedule_time{event_type->callback(
|
||||
evt.user_data, evt.time,
|
||||
std::chrono::nanoseconds{GetGlobalTimeNs().count() - evt.time})};
|
||||
|
||||
event_mutex.lock();
|
||||
pending_events.fetch_sub(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
if (evt.reschedule_time != 0) {
|
||||
event_queue.emplace_back(
|
||||
Event{global_timer + evt.reschedule_time - (global_timer - evt.time),
|
||||
event_fifo_id++, evt.user_data, evt.type, evt.reschedule_time});
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
if (evt.reschedule_time != 0) {
|
||||
// If this event was scheduled into a pause, its time now is going to be way behind.
|
||||
// Re-set this event to continue from the end of the pause.
|
||||
auto next_time{evt.time + evt.reschedule_time};
|
||||
if (evt.time < pause_end_time) {
|
||||
next_time = pause_end_time + evt.reschedule_time;
|
||||
}
|
||||
|
||||
const auto next_schedule_time{new_schedule_time.has_value()
|
||||
? new_schedule_time.value().count()
|
||||
: evt.reschedule_time};
|
||||
event_queue.emplace_back(
|
||||
Event{next_time, event_fifo_id++, evt.user_data, evt.type, next_schedule_time});
|
||||
pending_events.fetch_add(1, std::memory_order_relaxed);
|
||||
std::push_heap(event_queue.begin(), event_queue.end(), std::greater<>());
|
||||
}
|
||||
}
|
||||
|
||||
global_timer = GetGlobalTimeNs().count();
|
||||
|
||||
@@ -20,8 +20,9 @@
|
||||
namespace Core::Timing {
|
||||
|
||||
/// A callback that may be scheduled for a particular core timing event.
|
||||
using TimedCallback =
|
||||
std::function<void(std::uintptr_t user_data, std::chrono::nanoseconds ns_late)>;
|
||||
using TimedCallback = std::function<std::optional<std::chrono::nanoseconds>(
|
||||
std::uintptr_t user_data, s64 time, std::chrono::nanoseconds ns_late)>;
|
||||
using PauseCallback = std::function<void(bool paused)>;
|
||||
|
||||
/// Contains the characteristics of a particular event.
|
||||
struct EventType {
|
||||
@@ -93,18 +94,15 @@ public:
|
||||
|
||||
/// Schedules an event in core timing
|
||||
void ScheduleEvent(std::chrono::nanoseconds ns_into_future,
|
||||
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0);
|
||||
|
||||
/// Schedules an event with an absolute time in core timing
|
||||
void ScheduleEventAt(std::chrono::nanoseconds next_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data = 0);
|
||||
const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data = 0,
|
||||
bool absolute_time = false);
|
||||
|
||||
/// Schedules an event which will automatically re-schedule itself with the given time, until
|
||||
/// unscheduled
|
||||
void ScheduleLoopingEvent(std::chrono::nanoseconds time,
|
||||
void ScheduleLoopingEvent(std::chrono::nanoseconds start_time,
|
||||
std::chrono::nanoseconds resched_time,
|
||||
const std::shared_ptr<EventType>& event_type,
|
||||
std::uintptr_t user_data = 0);
|
||||
std::uintptr_t user_data = 0, bool absolute_time = false);
|
||||
|
||||
void UnscheduleEvent(const std::shared_ptr<EventType>& event_type, std::uintptr_t user_data);
|
||||
|
||||
@@ -136,6 +134,9 @@ public:
|
||||
/// Checks for events manually and returns time in nanoseconds for next event, threadsafe.
|
||||
std::optional<s64> Advance();
|
||||
|
||||
/// Register a callback function to be called when coretiming pauses.
|
||||
void RegisterPauseCallback(PauseCallback&& callback);
|
||||
|
||||
private:
|
||||
struct Event;
|
||||
|
||||
@@ -147,7 +148,7 @@ private:
|
||||
|
||||
std::unique_ptr<Common::WallClock> clock;
|
||||
|
||||
u64 global_timer = 0;
|
||||
s64 global_timer = 0;
|
||||
|
||||
// The queue is a min-heap using std::make_heap/push_heap/pop_heap.
|
||||
// We don't use std::priority_queue because we need to be able to serialize, unserialize and
|
||||
@@ -173,10 +174,13 @@ private:
|
||||
bool shutting_down{};
|
||||
bool is_multicore{};
|
||||
size_t pause_count{};
|
||||
s64 pause_end_time{};
|
||||
|
||||
/// Cycle timing
|
||||
u64 ticks{};
|
||||
s64 downcount{};
|
||||
|
||||
std::vector<PauseCallback> pause_callbacks{};
|
||||
};
|
||||
|
||||
/// Creates a core timing event with the given name and callback.
|
||||
|
||||
@@ -25,10 +25,8 @@ void CpuManager::ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager
|
||||
}
|
||||
|
||||
void CpuManager::Initialize() {
|
||||
running_mode = true;
|
||||
num_cores = is_multicore ? Core::Hardware::NUM_CPU_CORES : 1;
|
||||
gpu_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
||||
pause_barrier = std::make_unique<Common::Barrier>(num_cores + 1);
|
||||
|
||||
for (std::size_t core = 0; core < num_cores; core++) {
|
||||
core_data[core].host_thread = std::jthread(ThreadStart, std::ref(*this), core);
|
||||
@@ -36,8 +34,11 @@ void CpuManager::Initialize() {
|
||||
}
|
||||
|
||||
void CpuManager::Shutdown() {
|
||||
running_mode = false;
|
||||
Pause(false);
|
||||
for (std::size_t core = 0; core < num_cores; core++) {
|
||||
if (core_data[core].host_thread.joinable()) {
|
||||
core_data[core].host_thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::GuestThreadFunction() {
|
||||
@@ -64,6 +65,10 @@ void CpuManager::IdleThreadFunction() {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::ShutdownThreadFunction() {
|
||||
ShutdownThread();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
/// MultiCore ///
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
@@ -176,41 +181,13 @@ void CpuManager::PreemptSingleCore(bool from_running_enviroment) {
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::SuspendThread() {
|
||||
void CpuManager::ShutdownThread() {
|
||||
auto& kernel = system.Kernel();
|
||||
kernel.CurrentScheduler()->OnThreadStart();
|
||||
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
|
||||
auto* current_thread = kernel.GetCurrentEmuThread();
|
||||
|
||||
while (true) {
|
||||
auto core = is_multicore ? kernel.CurrentPhysicalCoreIndex() : 0;
|
||||
auto& scheduler = *kernel.CurrentScheduler();
|
||||
Kernel::KThread* current_thread = scheduler.GetSchedulerCurrentThread();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
|
||||
// This shouldn't be here. This is here because the scheduler needs the current
|
||||
// thread to have dispatch disabled before explicitly rescheduling. Ideally in the
|
||||
// future this will be called by RequestScheduleOnInterrupt and explicitly disabling
|
||||
// dispatch outside the scheduler will not be necessary.
|
||||
current_thread->DisableDispatch();
|
||||
|
||||
scheduler.RescheduleCurrentCore();
|
||||
}
|
||||
}
|
||||
|
||||
void CpuManager::Pause(bool paused) {
|
||||
std::scoped_lock lk{pause_lock};
|
||||
|
||||
if (pause_state == paused) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the new state
|
||||
pause_state.store(paused);
|
||||
|
||||
// Wake up any waiting threads
|
||||
pause_state.notify_all();
|
||||
|
||||
// Wait for all threads to successfully change state before returning
|
||||
pause_barrier->Sync();
|
||||
Common::Fiber::YieldTo(current_thread->GetHostContext(), *core_data[core].host_context);
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
void CpuManager::RunThread(std::size_t core) {
|
||||
@@ -241,27 +218,9 @@ void CpuManager::RunThread(std::size_t core) {
|
||||
system.GPU().ObtainContext();
|
||||
}
|
||||
|
||||
{
|
||||
// Set the current thread on entry
|
||||
auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
|
||||
Kernel::SetCurrentThread(system.Kernel(), current_thread);
|
||||
}
|
||||
|
||||
while (running_mode) {
|
||||
if (pause_state.load(std::memory_order_relaxed)) {
|
||||
// Wait for caller to acknowledge pausing
|
||||
pause_barrier->Sync();
|
||||
|
||||
// Wait until unpaused
|
||||
pause_state.wait(true, std::memory_order_relaxed);
|
||||
|
||||
// Wait for caller to acknowledge unpausing
|
||||
pause_barrier->Sync();
|
||||
}
|
||||
|
||||
auto current_thread = system.Kernel().CurrentScheduler()->GetSchedulerCurrentThread();
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
}
|
||||
auto* current_thread = system.Kernel().CurrentScheduler()->GetIdleThread();
|
||||
Kernel::SetCurrentThread(system.Kernel(), current_thread);
|
||||
Common::Fiber::YieldTo(data.host_context, *current_thread->GetHostContext());
|
||||
}
|
||||
|
||||
} // namespace Core
|
||||
|
||||
@@ -50,16 +50,14 @@ public:
|
||||
void Initialize();
|
||||
void Shutdown();
|
||||
|
||||
void Pause(bool paused);
|
||||
|
||||
std::function<void()> GetGuestThreadStartFunc() {
|
||||
return [this] { GuestThreadFunction(); };
|
||||
}
|
||||
std::function<void()> GetIdleThreadStartFunc() {
|
||||
return [this] { IdleThreadFunction(); };
|
||||
}
|
||||
std::function<void()> GetSuspendThreadStartFunc() {
|
||||
return [this] { SuspendThread(); };
|
||||
std::function<void()> GetShutdownThreadStartFunc() {
|
||||
return [this] { ShutdownThreadFunction(); };
|
||||
}
|
||||
|
||||
void PreemptSingleCore(bool from_running_enviroment = true);
|
||||
@@ -72,6 +70,7 @@ private:
|
||||
void GuestThreadFunction();
|
||||
void GuestRewindFunction();
|
||||
void IdleThreadFunction();
|
||||
void ShutdownThreadFunction();
|
||||
|
||||
void MultiCoreRunGuestThread();
|
||||
void MultiCoreRunGuestLoop();
|
||||
@@ -83,7 +82,7 @@ private:
|
||||
|
||||
static void ThreadStart(std::stop_token stop_token, CpuManager& cpu_manager, std::size_t core);
|
||||
|
||||
void SuspendThread();
|
||||
void ShutdownThread();
|
||||
void RunThread(std::size_t core);
|
||||
|
||||
struct CoreData {
|
||||
@@ -91,12 +90,7 @@ private:
|
||||
std::jthread host_thread;
|
||||
};
|
||||
|
||||
std::atomic<bool> running_mode{};
|
||||
std::atomic<bool> pause_state{};
|
||||
std::unique_ptr<Common::Barrier> pause_barrier{};
|
||||
std::unique_ptr<Common::Barrier> gpu_barrier{};
|
||||
std::mutex pause_lock{};
|
||||
|
||||
std::array<CoreData, Core::Hardware::NUM_CPU_CORES> core_data{};
|
||||
|
||||
bool is_async_gpu{};
|
||||
|
||||
@@ -269,7 +269,7 @@ Result KThread::InitializeIdleThread(Core::System& system, KThread* thread, s32
|
||||
Result KThread::InitializeHighPriorityThread(Core::System& system, KThread* thread,
|
||||
KThreadFunction func, uintptr_t arg, s32 virt_core) {
|
||||
return InitializeThread(thread, func, arg, {}, {}, virt_core, nullptr, ThreadType::HighPriority,
|
||||
system.GetCpuManager().GetSuspendThreadStartFunc());
|
||||
system.GetCpuManager().GetShutdownThreadStartFunc());
|
||||
}
|
||||
|
||||
Result KThread::InitializeUserThread(Core::System& system, KThread* thread, KThreadFunction func,
|
||||
@@ -739,6 +739,19 @@ void KThread::Continue() {
|
||||
KScheduler::OnThreadStateChanged(kernel, this, old_state);
|
||||
}
|
||||
|
||||
void KThread::WaitUntilSuspended() {
|
||||
// Make sure we have a suspend requested.
|
||||
ASSERT(IsSuspendRequested());
|
||||
|
||||
// Loop until the thread is not executing on any core.
|
||||
for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) {
|
||||
KThread* core_thread{};
|
||||
do {
|
||||
core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread();
|
||||
} while (core_thread == this);
|
||||
}
|
||||
}
|
||||
|
||||
Result KThread::SetActivity(Svc::ThreadActivity activity) {
|
||||
// Lock ourselves.
|
||||
KScopedLightLock lk(activity_pause_lock);
|
||||
|
||||
@@ -208,6 +208,8 @@ public:
|
||||
|
||||
void Continue();
|
||||
|
||||
void WaitUntilSuspended();
|
||||
|
||||
constexpr void SetSyncedIndex(s32 index) {
|
||||
synced_index = index;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ struct KernelCore::Impl {
|
||||
InitializeMemoryLayout();
|
||||
Init::InitializeKPageBufferSlabHeap(system);
|
||||
InitializeSchedulers();
|
||||
InitializeSuspendThreads();
|
||||
InitializeShutdownThreads();
|
||||
InitializePreemption(kernel);
|
||||
|
||||
RegisterHostThread();
|
||||
@@ -131,9 +131,9 @@ struct KernelCore::Impl {
|
||||
CleanupObject(system_resource_limit);
|
||||
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
if (suspend_threads[core_id]) {
|
||||
suspend_threads[core_id]->Close();
|
||||
suspend_threads[core_id] = nullptr;
|
||||
if (shutdown_threads[core_id]) {
|
||||
shutdown_threads[core_id]->Close();
|
||||
shutdown_threads[core_id] = nullptr;
|
||||
}
|
||||
|
||||
schedulers[core_id]->Finalize();
|
||||
@@ -238,26 +238,27 @@ struct KernelCore::Impl {
|
||||
|
||||
void InitializePreemption(KernelCore& kernel) {
|
||||
preemption_event = Core::Timing::CreateEvent(
|
||||
"PreemptionCallback", [this, &kernel](std::uintptr_t, std::chrono::nanoseconds) {
|
||||
"PreemptionCallback",
|
||||
[this, &kernel](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||
{
|
||||
KScopedSchedulerLock lock(kernel);
|
||||
global_scheduler_context->PreemptThreads();
|
||||
}
|
||||
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
const auto time_interval = std::chrono::nanoseconds{std::chrono::milliseconds(10)};
|
||||
system.CoreTiming().ScheduleEvent(time_interval, preemption_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(time_interval, time_interval, preemption_event);
|
||||
}
|
||||
|
||||
void InitializeSuspendThreads() {
|
||||
void InitializeShutdownThreads() {
|
||||
for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
|
||||
suspend_threads[core_id] = KThread::Create(system.Kernel());
|
||||
ASSERT(KThread::InitializeHighPriorityThread(system, suspend_threads[core_id], {}, {},
|
||||
shutdown_threads[core_id] = KThread::Create(system.Kernel());
|
||||
ASSERT(KThread::InitializeHighPriorityThread(system, shutdown_threads[core_id], {}, {},
|
||||
core_id)
|
||||
.IsSuccess());
|
||||
suspend_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
|
||||
shutdown_threads[core_id]->SetName(fmt::format("SuspendThread:{}", core_id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,7 +780,7 @@ struct KernelCore::Impl {
|
||||
std::weak_ptr<ServiceThread> default_service_thread;
|
||||
Common::ThreadWorker service_threads_manager;
|
||||
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> suspend_threads;
|
||||
std::array<KThread*, Core::Hardware::NUM_CPU_CORES> shutdown_threads;
|
||||
std::array<Core::CPUInterruptHandler, Core::Hardware::NUM_CPU_CORES> interrupts{};
|
||||
std::array<std::unique_ptr<Kernel::KScheduler>, Core::Hardware::NUM_CPU_CORES> schedulers{};
|
||||
|
||||
@@ -1093,16 +1094,27 @@ const Kernel::KSharedMemory& KernelCore::GetHidBusSharedMem() const {
|
||||
|
||||
void KernelCore::Suspend(bool suspended) {
|
||||
const bool should_suspend{exception_exited || suspended};
|
||||
const auto state{should_suspend ? ThreadState::Runnable : ThreadState::Waiting};
|
||||
{
|
||||
KScopedSchedulerLock lk{*this};
|
||||
for (auto* thread : impl->suspend_threads) {
|
||||
thread->SetState(state);
|
||||
thread->SetWaitReasonForDebugging(ThreadWaitReasonForDebugging::Suspended);
|
||||
const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable;
|
||||
|
||||
for (auto* process : GetProcessList()) {
|
||||
process->SetActivity(activity);
|
||||
|
||||
if (should_suspend) {
|
||||
// Wait for execution to stop
|
||||
for (auto* thread : process->GetThreadList()) {
|
||||
thread->WaitUntilSuspended();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KernelCore::ShutdownCores() {
|
||||
for (auto* thread : impl->shutdown_threads) {
|
||||
void(thread->Run());
|
||||
}
|
||||
InterruptAllPhysicalCores();
|
||||
}
|
||||
|
||||
bool KernelCore::IsMulticore() const {
|
||||
return impl->is_multicore;
|
||||
}
|
||||
|
||||
@@ -283,6 +283,9 @@ public:
|
||||
/// Exceptional exit all processes.
|
||||
void ExceptionalExit();
|
||||
|
||||
/// Notify emulated CPU cores to shut down.
|
||||
void ShutdownCores();
|
||||
|
||||
bool IsMulticore() const;
|
||||
|
||||
bool IsShuttingDown() const;
|
||||
|
||||
@@ -11,15 +11,17 @@
|
||||
namespace Kernel {
|
||||
|
||||
TimeManager::TimeManager(Core::System& system_) : system{system_} {
|
||||
time_manager_event_type =
|
||||
Core::Timing::CreateEvent("Kernel::TimeManagerCallback",
|
||||
[this](std::uintptr_t thread_handle, std::chrono::nanoseconds) {
|
||||
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
|
||||
{
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
thread->OnTimer();
|
||||
}
|
||||
});
|
||||
time_manager_event_type = Core::Timing::CreateEvent(
|
||||
"Kernel::TimeManagerCallback",
|
||||
[this](std::uintptr_t thread_handle, s64 time,
|
||||
std::chrono::nanoseconds) -> std::optional<std::chrono::nanoseconds> {
|
||||
KThread* thread = reinterpret_cast<KThread*>(thread_handle);
|
||||
{
|
||||
KScopedSchedulerLock sl(system.Kernel());
|
||||
thread->OnTimer();
|
||||
}
|
||||
return std::nullopt;
|
||||
});
|
||||
}
|
||||
|
||||
void TimeManager::ScheduleTimeEvent(KThread* thread, s64 nanoseconds) {
|
||||
|
||||
@@ -74,26 +74,34 @@ IAppletResource::IAppletResource(Core::System& system_,
|
||||
// Register update callbacks
|
||||
pad_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdatePadCallback",
|
||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateControllers(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
mouse_keyboard_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateMouseKeyboardCallback",
|
||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateMouseKeyboard(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
motion_update_event = Core::Timing::CreateEvent(
|
||||
"HID::UpdateMotionCallback",
|
||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateMotion(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
|
||||
system.CoreTiming().ScheduleEvent(mouse_keyboard_update_ns, mouse_keyboard_update_event);
|
||||
system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(pad_update_ns, pad_update_ns, pad_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(mouse_keyboard_update_ns, mouse_keyboard_update_ns,
|
||||
mouse_keyboard_update_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(motion_update_ns, motion_update_ns,
|
||||
motion_update_event);
|
||||
|
||||
system.HIDCore().ReloadInputDevices();
|
||||
}
|
||||
@@ -135,13 +143,6 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
|
||||
}
|
||||
controller->OnUpdate(core_timing);
|
||||
}
|
||||
|
||||
// If ns_late is higher than the update rate ignore the delay
|
||||
if (ns_late > pad_update_ns) {
|
||||
ns_late = {};
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
|
||||
@@ -150,26 +151,12 @@ void IAppletResource::UpdateMouseKeyboard(std::uintptr_t user_data,
|
||||
|
||||
controllers[static_cast<size_t>(HidController::Mouse)]->OnUpdate(core_timing);
|
||||
controllers[static_cast<size_t>(HidController::Keyboard)]->OnUpdate(core_timing);
|
||||
|
||||
// If ns_late is higher than the update rate ignore the delay
|
||||
if (ns_late > mouse_keyboard_update_ns) {
|
||||
ns_late = {};
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(mouse_keyboard_update_ns - ns_late, mouse_keyboard_update_event);
|
||||
}
|
||||
|
||||
void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
controllers[static_cast<size_t>(HidController::NPad)]->OnMotionUpdate(core_timing);
|
||||
|
||||
// If ns_late is higher than the update rate ignore the delay
|
||||
if (ns_late > motion_update_ns) {
|
||||
ns_late = {};
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
|
||||
}
|
||||
|
||||
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
|
||||
|
||||
@@ -50,12 +50,15 @@ HidBus::HidBus(Core::System& system_)
|
||||
// Register update callbacks
|
||||
hidbus_update_event = Core::Timing::CreateEvent(
|
||||
"Hidbus::UpdateCallback",
|
||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto guard = LockService();
|
||||
UpdateHidbus(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
|
||||
system_.CoreTiming().ScheduleEvent(hidbus_update_ns, hidbus_update_event);
|
||||
system_.CoreTiming().ScheduleLoopingEvent(hidbus_update_ns, hidbus_update_ns,
|
||||
hidbus_update_event);
|
||||
}
|
||||
|
||||
HidBus::~HidBus() {
|
||||
@@ -63,8 +66,6 @@ HidBus::~HidBus() {
|
||||
}
|
||||
|
||||
void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
auto& core_timing = system.CoreTiming();
|
||||
|
||||
if (is_hidbus_enabled) {
|
||||
for (std::size_t i = 0; i < devices.size(); ++i) {
|
||||
if (!devices[i].is_device_initializated) {
|
||||
@@ -82,13 +83,6 @@ void HidBus::UpdateHidbus(std::uintptr_t user_data, std::chrono::nanoseconds ns_
|
||||
sizeof(HidbusStatusManagerEntry));
|
||||
}
|
||||
}
|
||||
|
||||
// If ns_late is higher than the update rate ignore the delay
|
||||
if (ns_late > hidbus_update_ns) {
|
||||
ns_late = {};
|
||||
}
|
||||
|
||||
core_timing.ScheduleEvent(hidbus_update_ns - ns_late, hidbus_update_event);
|
||||
}
|
||||
|
||||
std::optional<std::size_t> HidBus::GetDeviceIndexFromHandle(BusHandle handle) const {
|
||||
|
||||
@@ -69,21 +69,20 @@ NVFlinger::NVFlinger(Core::System& system_, HosBinderDriverServer& hos_binder_dr
|
||||
|
||||
// Schedule the screen composition events
|
||||
composition_event = Core::Timing::CreateEvent(
|
||||
"ScreenComposition", [this](std::uintptr_t, std::chrono::nanoseconds ns_late) {
|
||||
"ScreenComposition",
|
||||
[this](std::uintptr_t, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
const auto lock_guard = Lock();
|
||||
Compose();
|
||||
|
||||
const auto ticks = std::chrono::nanoseconds{GetNextTicks()};
|
||||
const auto ticks_delta = ticks - ns_late;
|
||||
const auto future_ns = std::max(std::chrono::nanoseconds::zero(), ticks_delta);
|
||||
|
||||
this->system.CoreTiming().ScheduleEvent(future_ns, composition_event);
|
||||
return std::max(std::chrono::nanoseconds::zero(),
|
||||
std::chrono::nanoseconds(GetNextTicks()) - ns_late);
|
||||
});
|
||||
|
||||
if (system.IsMulticore()) {
|
||||
vsync_thread = std::jthread([this](std::stop_token token) { SplitVSync(token); });
|
||||
} else {
|
||||
system.CoreTiming().ScheduleEvent(frame_ns, composition_event);
|
||||
system.CoreTiming().ScheduleLoopingEvent(frame_ns, frame_ns, composition_event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -184,10 +184,12 @@ CheatEngine::~CheatEngine() {
|
||||
void CheatEngine::Initialize() {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"CheatEngine::FrameCallback::" + Common::HexToString(metadata.main_nso_build_id),
|
||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
core_timing.ScheduleEvent(CHEAT_ENGINE_NS, event);
|
||||
core_timing.ScheduleLoopingEvent(CHEAT_ENGINE_NS, CHEAT_ENGINE_NS, event);
|
||||
|
||||
metadata.process_id = system.CurrentProcess()->GetProcessID();
|
||||
metadata.title_id = system.GetCurrentProcessProgramID();
|
||||
@@ -237,8 +239,6 @@ void CheatEngine::FrameCallback(std::uintptr_t, std::chrono::nanoseconds ns_late
|
||||
MICROPROFILE_SCOPE(Cheat_Engine);
|
||||
|
||||
vm.Execute(metadata);
|
||||
|
||||
core_timing.ScheduleEvent(CHEAT_ENGINE_NS - ns_late, event);
|
||||
}
|
||||
|
||||
} // namespace Core::Memory
|
||||
|
||||
@@ -53,8 +53,10 @@ Freezer::Freezer(Core::Timing::CoreTiming& core_timing_, Core::Memory::Memory& m
|
||||
: core_timing{core_timing_}, memory{memory_} {
|
||||
event = Core::Timing::CreateEvent(
|
||||
"MemoryFreezer::FrameCallback",
|
||||
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
|
||||
[this](std::uintptr_t user_data, s64 time,
|
||||
std::chrono::nanoseconds ns_late) -> std::optional<std::chrono::nanoseconds> {
|
||||
FrameCallback(user_data, ns_late);
|
||||
return std::nullopt;
|
||||
});
|
||||
core_timing.ScheduleEvent(memory_freezer_ns, event);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user