Compare commits

..

1 Commits

Author SHA1 Message Date
xbzk 74b5e10dc5 [native] doesUpdateMatchProgram update mask fix (#4465)
- [x] I have read and followed the [Contribution Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/CONTRIBUTING.md#code-contributions).
- [x] I have read and followed the [AI Policy](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/AI.md)
- [x] I have read and followed the [Coding Guidelines](https://git.eden-emu.dev/eden-emu/eden/src/branch/master/docs/policies/Coding.md) to the best of my ability.

-------------------

Old silly bug i've been neglecting:
On Android, installing content to NAND, under some content id conditions (ie MK8D DLC), base/update IDs mismatch, and UI prompts "The content that you selected does not match this game. Install anyway?
Useless, yet fixed.

Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4465
Reviewed-by: lizzie <lizzie@eden-emu.dev>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
2026-09-20 22:56:06 +02:00
33 changed files with 330 additions and 454 deletions
+2 -2
View File
@@ -788,7 +788,7 @@ int Java_org_yuzu_yuzu_1emu_NativeLibrary_installFileToNand(JNIEnv* env, jobject
jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj, jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* env, jobject jobj,
jstring jprogramId, jstring jprogramId,
jstring jupdatePath) { jstring jupdatePath) {
u64 program_id = EmulationSession::GetProgramId(env, jprogramId); const u64 program_id = FileSys::GetBaseTitleID(EmulationSession::GetProgramId(env, jprogramId));
std::string updatePath = Common::Android::GetJString(env, jupdatePath); std::string updatePath = Common::Android::GetJString(env, jupdatePath);
std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>( std::shared_ptr<FileSys::NSP> nsp = std::make_shared<FileSys::NSP>(
EmulationSession::GetInstance().System().GetFilesystem()->OpenFile( EmulationSession::GetInstance().System().GetFilesystem()->OpenFile(
@@ -796,7 +796,7 @@ jboolean Java_org_yuzu_yuzu_1emu_NativeLibrary_doesUpdateMatchProgram(JNIEnv* en
for (const auto& item : nsp->GetNCAs()) { for (const auto& item : nsp->GetNCAs()) {
for (const auto& nca_details : item.second) { for (const auto& nca_details : item.second) {
if (nca_details.second->GetName().ends_with(".cnmt.nca")) { if (nca_details.second->GetName().ends_with(".cnmt.nca")) {
auto update_id = nca_details.second->GetTitleId() & ~0xFFFULL; const auto update_id = FileSys::GetBaseTitleID(nca_details.second->GetTitleId());
if (update_id == program_id) { if (update_id == program_id) {
return true; return true;
} }
+1 -5
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -19,10 +19,6 @@ ADSP::ADSP(Core::System& system, Sink::Sink& sink) {
} }
} }
void ADSP::NotifyShutdown() {
opus_decoder->NotifyShutdown();
}
AudioRenderer::AudioRenderer& ADSP::AudioRenderer() { AudioRenderer::AudioRenderer& ADSP::AudioRenderer() {
return *audio_renderer; return *audio_renderer;
} }
+1 -2
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -45,7 +45,6 @@ public:
explicit ADSP(Core::System& system, Sink::Sink& sink); explicit ADSP(Core::System& system, Sink::Sink& sink);
~ADSP() = default; ~ADSP() = default;
void NotifyShutdown();
AudioRenderer::AudioRenderer& AudioRenderer(); AudioRenderer::AudioRenderer& AudioRenderer();
OpusDecoder::OpusDecoder& OpusDecoder(); OpusDecoder::OpusDecoder& OpusDecoder();
@@ -82,9 +82,6 @@ void OpusDecoder::Main(std::stop_token stop_token) {
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
auto msg = Receive(Direction::DSP, stop_token); auto msg = Receive(Direction::DSP, stop_token);
if (stop_token.stop_requested())
break;
switch (msg) { switch (msg) {
case Shutdown: case Shutdown:
Send(Direction::Host, Message::ShutdownOK); Send(Direction::Host, Message::ShutdownOK);
@@ -269,9 +266,4 @@ void OpusDecoder::Main(std::stop_token stop_token) {
} }
} }
void OpusDecoder::NotifyShutdown() {
init_thread.request_stop();
main_thread.request_stop();
}
} // namespace AudioCore::ADSP::OpusDecoder } // namespace AudioCore::ADSP::OpusDecoder
@@ -67,8 +67,6 @@ public:
shared_memory = &shared_memory_; shared_memory = &shared_memory_;
} }
void NotifyShutdown();
private: private:
/** /**
* Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread. * Initializing thread, launched at audio_core boot to avoid blocking the main emu boot thread.
-5
View File
@@ -31,11 +31,6 @@ void AudioCore::CreateSinks() {
input_sink = Sink::CreateSinkFromID(sink_id.GetValue(), audio_input_device_id.GetValue()); input_sink = Sink::CreateSinkFromID(sink_id.GetValue(), audio_input_device_id.GetValue());
} }
void AudioCore::NotifyShutdown() {
audio_manager->NotifyShutdown();
adsp->NotifyShutdown();
}
void AudioCore::Shutdown() { void AudioCore::Shutdown() {
audio_manager->Shutdown(); audio_manager->Shutdown();
} }
-2
View File
@@ -24,8 +24,6 @@ public:
explicit AudioCore(Core::System& system); explicit AudioCore(Core::System& system);
~AudioCore(); ~AudioCore();
void NotifyShutdown();
/** /**
* Shutdown the audio core. * Shutdown the audio core.
*/ */
+1 -6
View File
@@ -17,7 +17,7 @@ AudioManager::AudioManager() {
std::unique_lock l{events.GetAudioEventLock()}; std::unique_lock l{events.GetAudioEventLock()};
events.ClearEvents(); events.ClearEvents();
while (!stop_token.stop_requested()) { while (!stop_token.stop_requested()) {
const auto timed_out = events.Wait(l, std::chrono::seconds{2}); const auto timed_out{events.Wait(l, std::chrono::seconds(2))};
if (events.CheckAudioEventSet(Event::Type::Max)) { if (events.CheckAudioEventSet(Event::Type::Max)) {
break; break;
} }
@@ -34,11 +34,6 @@ AudioManager::AudioManager() {
}); });
} }
void AudioManager::NotifyShutdown() {
events.SetAudioEvent(Event::Type::Max, true);
thread.request_stop();
}
void AudioManager::Shutdown() { void AudioManager::Shutdown() {
events.SetAudioEvent(Event::Type::Max, true); events.SetAudioEvent(Event::Type::Max, true);
if (thread.joinable()) { if (thread.joinable()) {
+3 -4
View File
@@ -39,10 +39,9 @@ class AudioManager {
public: public:
explicit AudioManager(); explicit AudioManager();
/// @brief Notify of impending shutdown /**
void NotifyShutdown(); * Shutdown the audio manager.
*/
/// @brief Shutdown the audio manager.
void Shutdown(); void Shutdown();
/** /**
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -38,7 +35,7 @@ void CircularBufferSinkInfo::Update(BehaviorInfo::ErrorInfo& error_info, OutStat
auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())}; auto current_params{reinterpret_cast<CircularBufferInParameter*>(parameter.data())};
auto current_state{reinterpret_cast<CircularBufferState*>(state.data())}; auto current_state{reinterpret_cast<CircularBufferState*>(state.data())};
if (in_use == bool(buffer_params->in_use) && !buffer_unmapped) { if (in_use == buffer_params->in_use && !buffer_unmapped) {
error_info.error_code = ResultSuccess; error_info.error_code = ResultSuccess;
error_info.address = CpuAddr(0); error_info.address = CpuAddr(0);
out_status.writeOffset = current_state->last_pos2; out_status.writeOffset = current_state->last_pos2;
+20 -19
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
@@ -31,10 +31,10 @@ public:
}; };
struct DeviceInParameter { struct DeviceInParameter {
/* 0x000 */ u8 name[0x100]; /* 0x000 */ char name[0x100];
/* 0x100 */ u32 input_count; /* 0x100 */ u32 input_count;
/* 0x104 */ std::array<s8, MaxChannels> inputs; /* 0x104 */ std::array<s8, MaxChannels> inputs;
/* 0x10A */ u8 unk10A[0x1]; /* 0x10A */ char unk10A[0x1];
/* 0x10B */ bool downmix_enabled; /* 0x10B */ bool downmix_enabled;
/* 0x10C */ std::array<f32, 4> downmix_coeff; /* 0x10C */ std::array<f32, 4> downmix_coeff;
}; };
@@ -43,7 +43,7 @@ public:
struct DeviceState { struct DeviceState {
/* 0x00 */ UpsamplerInfo* upsampler_info; /* 0x00 */ UpsamplerInfo* upsampler_info;
/* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff; /* 0x08 */ std::array<Common::FixedPoint<16, 16>, 4> downmix_coeff;
/* 0x18 */ u8 unk18[0x18]; /* 0x18 */ char unk18[0x18];
}; };
static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!"); static_assert(sizeof(DeviceState) == 0x30, "DeviceState has the wrong size!");
@@ -55,8 +55,8 @@ public:
/* 0x14 */ u32 previous_pos; /* 0x14 */ u32 previous_pos;
/* 0x18 */ SampleFormat format; /* 0x18 */ SampleFormat format;
/* 0x1C */ std::array<s8, MaxChannels> inputs; /* 0x1C */ std::array<s8, MaxChannels> inputs;
/* 0x22 */ u8 in_use; /* 0x22 */ bool in_use;
/* 0x23 */ u8 unk23[0x5]; /* 0x23 */ char unk23[0x5];
}; };
static_assert(sizeof(CircularBufferInParameter) == 0x28, static_assert(sizeof(CircularBufferInParameter) == 0x28,
"CircularBufferInParameter has the wrong size!"); "CircularBufferInParameter has the wrong size!");
@@ -65,16 +65,16 @@ public:
/* 0x00 */ u32 last_pos2; /* 0x00 */ u32 last_pos2;
/* 0x04 */ s32 current_pos; /* 0x04 */ s32 current_pos;
/* 0x08 */ u32 last_pos; /* 0x08 */ u32 last_pos;
/* 0x0C */ u8 unk0C[0x4]; /* 0x0C */ char unk0C[0x4];
/* 0x10 */ AddressInfo address_info; /* 0x10 */ AddressInfo address_info;
}; };
static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!"); static_assert(sizeof(CircularBufferState) == 0x30, "CircularBufferState has the wrong size!");
struct InParameter { struct InParameter {
/* 0x000 */ Type type; /* 0x000 */ Type type;
/* 0x001 */ u8 in_use; /* 0x001 */ bool in_use;
/* 0x004 */ u32 node_id; /* 0x004 */ u32 node_id;
/* 0x008 */ u8 unk08[0x18]; /* 0x008 */ char unk08[0x18];
union { union {
/* 0x020 */ DeviceInParameter device; /* 0x020 */ DeviceInParameter device;
/* 0x020 */ CircularBufferInParameter circular_buffer; /* 0x020 */ CircularBufferInParameter circular_buffer;
@@ -84,7 +84,7 @@ public:
struct OutStatus { struct OutStatus {
/* 0x00 */ u32 writeOffset; /* 0x00 */ u32 writeOffset;
/* 0x04 */ u8 unk04[0x1C]; /* 0x04 */ char unk04[0x1C];
}; // size == 0x20 }; // size == 0x20
static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!"); static_assert(sizeof(OutStatus) == 0x20, "SinkInfoBase::OutStatus has the wrong size!");
@@ -162,18 +162,19 @@ public:
u8* GetParameter(); u8* GetParameter();
protected: protected:
/// Type of this sink
Type type{Type::Invalid};
/// Is this sink in use?
bool in_use{};
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped{};
/// Node id for this sink
u32 node_id{};
/// State buffer for this sink /// State buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{}; std::array<u8, (std::max)(sizeof(DeviceState), sizeof(CircularBufferState))> state{};
/// Parameter buffer for this sink /// Parameter buffer for this sink
std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))> parameter{}; std::array<u8, (std::max)(sizeof(DeviceInParameter), sizeof(CircularBufferInParameter))>
/// Type of this sink parameter{};
Type type{Type::Invalid};
/// Node id for this sink
u32 node_id{};
/// Is this sink in use?
bool in_use : 1 = false;
/// Is this sink's buffer unmapped? Circular only
bool buffer_unmapped : 1 = false;
}; };
} // namespace AudioCore::Renderer } // namespace AudioCore::Renderer
+21 -46
View File
@@ -110,13 +110,8 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl { struct System::Impl {
explicit Impl(System& system) explicit Impl(System& system)
: kernel{system} : kernel{system}, fs_controller{system}, hid_core{kernel}, cpu_manager{system},
, fs_controller{system} reporter{system}, applet_manager{system}, frontend_applets{system}, profile_manager{} {}
, hid_core{kernel}
, cpu_manager{system}
, reporter{system}
, profile_manager{}
{}
u64 program_id; u64 program_id;
@@ -129,12 +124,6 @@ struct System::Impl {
core_timing.SetMulticore(is_multicore); core_timing.SetMulticore(is_multicore);
core_timing.Initialize([&system]() { system.RegisterHostThread(); }); core_timing.Initialize([&system]() { system.RegisterHostThread(); });
applet_manager.emplace(system);
frontend_applets.emplace(system);
apm_controller.emplace(core_timing);
arp_manager.emplace();
profile_manager.emplace();
// Create a default fs if one doesn't already exist. // Create a default fs if one doesn't already exist.
if (virtual_filesystem == nullptr) { if (virtual_filesystem == nullptr) {
virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>(); virtual_filesystem = std::make_shared<FileSys::RealVfsFilesystem>();
@@ -144,7 +133,7 @@ struct System::Impl {
} }
// Create default implementations of applets if one is not provided. // Create default implementations of applets if one is not provided.
frontend_applets->SetDefaultAppletsIfMissing(); frontend_applets.SetDefaultAppletsIfMissing();
auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue(); auto const is_async_gpu = Settings::values.use_asynchronous_gpu_emulation.GetValue();
@@ -387,7 +376,7 @@ struct System::Impl {
// Register with applet manager // Register with applet manager
// All threads are started, begin main process execution, now that we're in the clear // All threads are started, begin main process execution, now that we're in the clear
applet_manager->CreateAndInsertByFrontendAppletParameters(std::move(process), params); applet_manager.CreateAndInsertByFrontendAppletParameters(std::move(process), params);
if (Settings::values.gamecard_inserted) { if (Settings::values.gamecard_inserted) {
if (Settings::values.gamecard_current_game) { if (Settings::values.gamecard_current_game) {
@@ -439,27 +428,13 @@ struct System::Impl {
core_timing.SyncPause(false); core_timing.SyncPause(false);
Network::CancelPendingSocketOperations(); Network::CancelPendingSocketOperations();
kernel.SuspendEmulation(true); kernel.SuspendEmulation(true);
kernel.ShutdownCores();
// Notify services helpers of shutdown
audio_core->NotifyShutdown();
// Wait for threads/services to join
kernel.CloseServices(); kernel.CloseServices();
kernel.ShutdownCores();
// service shutdown
services.reset(); services.reset();
service_manager.reset(); service_manager.reset();
frontend_applets.reset();
applet_manager.reset();
apm_controller.reset();
arp_manager.reset();
profile_manager.reset();
fs_controller.Reset(); fs_controller.Reset();
cheat_engine.reset(); cheat_engine.reset();
core_timing.ClearPendingEvents(); core_timing.ClearPendingEvents();
core_timing.Reset();
app_loader.reset(); app_loader.reset();
audio_core.reset(); audio_core.reset();
gpu_core.reset(); gpu_core.reset();
@@ -477,7 +452,7 @@ struct System::Impl {
} }
// Reset all glue registrations // Reset all glue registrations
arp_manager->ResetAll(); arp_manager.ResetAll();
LOG_DEBUG(Core, "Shutdown OK"); LOG_DEBUG(Core, "Shutdown OK");
} }
@@ -507,13 +482,13 @@ struct System::Impl {
CpuManager cpu_manager; CpuManager cpu_manager;
Reporter reporter; Reporter reporter;
/// Applets /// Applets
std::optional<Service::AM::AppletManager> applet_manager; Service::AM::AppletManager applet_manager;
std::optional<Service::AM::Frontend::FrontendAppletHolder> frontend_applets; Service::AM::Frontend::FrontendAppletHolder frontend_applets;
/// APM (Performance) services /// APM (Performance) services
std::optional<Service::APM::Controller> apm_controller; Service::APM::Controller apm_controller{core_timing};
/// Service State /// Service State
std::optional<Service::Glue::ARPManager> arp_manager; Service::Glue::ARPManager arp_manager;
std::optional<Service::Account::ProfileManager> profile_manager; Service::Account::ProfileManager profile_manager;
/// Network instance /// Network instance
Network::NetworkInstance network_instance; Network::NetworkInstance network_instance;
Core::SpeedLimiter speed_limiter; Core::SpeedLimiter speed_limiter;
@@ -840,19 +815,19 @@ void System::RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
} }
void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) { void System::SetFrontendAppletSet(Service::AM::Frontend::FrontendAppletSet&& set) {
impl->frontend_applets->SetFrontendAppletSet(std::move(set)); impl->frontend_applets.SetFrontendAppletSet(std::move(set));
} }
Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() { Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() {
return *impl->frontend_applets; return impl->frontend_applets;
} }
const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const { const Service::AM::Frontend::FrontendAppletHolder& System::GetFrontendAppletHolder() const {
return *impl->frontend_applets; return impl->frontend_applets;
} }
Service::AM::AppletManager& System::GetAppletManager() { Service::AM::AppletManager& System::GetAppletManager() {
return *impl->applet_manager; return impl->applet_manager;
} }
void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) { void System::SetContentProvider(std::unique_ptr<FileSys::ContentProviderUnion> provider) {
@@ -893,27 +868,27 @@ const Reporter& System::GetReporter() const {
} }
Service::Glue::ARPManager& System::GetARPManager() { Service::Glue::ARPManager& System::GetARPManager() {
return *impl->arp_manager; return impl->arp_manager;
} }
const Service::Glue::ARPManager& System::GetARPManager() const { const Service::Glue::ARPManager& System::GetARPManager() const {
return *impl->arp_manager; return impl->arp_manager;
} }
Service::APM::Controller& System::GetAPMController() { Service::APM::Controller& System::GetAPMController() {
return *impl->apm_controller; return impl->apm_controller;
} }
const Service::APM::Controller& System::GetAPMController() const { const Service::APM::Controller& System::GetAPMController() const {
return *impl->apm_controller; return impl->apm_controller;
} }
Service::Account::ProfileManager& System::GetProfileManager() { Service::Account::ProfileManager& System::GetProfileManager() {
return *impl->profile_manager; return impl->profile_manager;
} }
const Service::Account::ProfileManager& System::GetProfileManager() const { const Service::Account::ProfileManager& System::GetProfileManager() const {
return *impl->profile_manager; return impl->profile_manager;
} }
void System::SetExitLocked(bool locked) { void System::SetExitLocked(bool locked) {
+2 -2
View File
@@ -304,10 +304,10 @@ std::optional<s64> CoreTiming::Advance() {
void CoreTiming::Reset() { void CoreTiming::Reset() {
paused = true; paused = true;
pause_event.Set();
event.Set();
if (timer_thread.joinable()) { if (timer_thread.joinable()) {
timer_thread.request_stop(); timer_thread.request_stop();
pause_event.Set();
event.Set();
timer_thread.join(); timer_thread.join();
} }
has_started = false; has_started = false;
@@ -72,9 +72,11 @@ void GlobalSchedulerContext::UnregisterDummyThreadForWakeup(KThread* thread) noe
void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept { void GlobalSchedulerContext::WakeupWaitingDummyThreads(KernelCore& kernel) noexcept {
ASSERT(this->IsLocked()); ASSERT(this->IsLocked());
for (auto* thread : m_woken_dummy_threads) if (m_woken_dummy_threads.size() > 0) {
thread->DummyThreadEndWait(kernel); for (auto* thread : m_woken_dummy_threads)
m_woken_dummy_threads.clear(); thread->DummyThreadEndWait(kernel);
m_woken_dummy_threads.clear();
}
} }
} // namespace Kernel } // namespace Kernel
+1
View File
@@ -59,6 +59,7 @@ Result TerminateChildren(KernelCore& kernel, KProcess* process, const KThread* t
KThread* cur_child = nullptr; KThread* cur_child = nullptr;
{ {
KScopedLightLock proc_lk(process->GetListLock()); KScopedLightLock proc_lk(process->GetListLock());
auto& thread_list = process->GetThreadList(); auto& thread_list = process->GetThreadList();
for (auto it = thread_list.begin(); it != thread_list.end(); ++it) { for (auto it = thread_list.begin(); it != thread_list.end(); ++it) {
if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) { if (KThread* thread = std::addressof(*it); thread != thread_to_not_terminate) {
+7 -3
View File
@@ -1236,10 +1236,14 @@ namespace Kernel {
KScopedSchedulerLock sl{kernel}; KScopedSchedulerLock sl{kernel};
// Determine if this is the first termination request. // Determine if this is the first termination request.
// Perform an atomic compare-and-swap from false to true. const bool first_request = [&]() -> bool {
bool expected = false; // Perform an atomic compare-and-swap from false to true.
bool expected = false;
return m_termination_requested.compare_exchange_strong(expected, true);
}();
// If this is the first request, start termination procedure. // If this is the first request, start termination procedure.
if (m_termination_requested.compare_exchange_strong(expected, true)) { if (first_request) {
// If the thread is in initialized state, just change state to terminated. // If the thread is in initialized state, just change state to terminated.
if (this->GetState() == ThreadState::Initialized) { if (this->GetState() == ThreadState::Initialized) {
m_thread_state = ThreadState::Terminated; m_thread_state = ThreadState::Terminated;
+5 -35
View File
@@ -4,7 +4,6 @@
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <thread>
#include "common/assert.h" #include "common/assert.h"
#include "core/hle/kernel/k_process.h" #include "core/hle/kernel/k_process.h"
#include "core/hle/kernel/k_thread.h" #include "core/hle/kernel/k_thread.h"
@@ -27,15 +26,7 @@ void KWorkerTask::DoWorkerTask(KernelCore& kernel) {
} }
} }
KWorkerTaskManager::KWorkerTaskManager() {} KWorkerTaskManager::KWorkerTaskManager() : m_waiting_thread(1, "KWorkerTaskManager") {}
KWorkerTaskManager::~KWorkerTaskManager() {
if (m_waiting_thread.joinable()) {
m_waiting_thread.request_stop();
m_task_cv.notify_one();
m_waiting_thread.join();
}
}
void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task) {
ASSERT(type <= WorkerType::Count); ASSERT(type <= WorkerType::Count);
@@ -44,31 +35,10 @@ void KWorkerTaskManager::AddTask(KernelCore& kernel, WorkerType type, KWorkerTas
void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) { void KWorkerTaskManager::AddTask(KernelCore& kernel, KWorkerTask* task) {
KScopedSchedulerLock sl(kernel); KScopedSchedulerLock sl(kernel);
m_waiting_thread.QueueWork([&kernel, task]() {
// spawn thread on demand // Do the task.
if (!m_waiting_thread.joinable()) { task->DoWorkerTask(kernel);
LOG_INFO(Kernel, "spawning KWorkerTaskManager thread"); });
m_waiting_thread = std::jthread([&kernel, this](std::stop_token stop_token) {
while (!stop_token.stop_requested()) {
KWorkerTask* t;
{
std::unique_lock lk{m_task_mutex};
m_task_cv.wait(lk);
if (stop_token.stop_requested())
break;
t = m_task_queue.back();
m_task_queue.pop_back();
}
t->DoWorkerTask(kernel);
}
});
}
{
std::scoped_lock lk{m_task_mutex};
m_task_queue.emplace_back(task);
}
m_task_cv.notify_one();
} }
} // namespace Kernel } // namespace Kernel
+3 -10
View File
@@ -1,13 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2022 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#pragma once #pragma once
#include <condition_variable>
#include <thread>
#include "common/common_types.h" #include "common/common_types.h"
#include "common/thread_worker.h" #include "common/thread_worker.h"
@@ -24,16 +19,14 @@ public:
}; };
KWorkerTaskManager(); KWorkerTaskManager();
~KWorkerTaskManager();
static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task); static void AddTask(KernelCore& kernel, WorkerType type, KWorkerTask* task);
private: private:
void AddTask(KernelCore& kernel, KWorkerTask* task); void AddTask(KernelCore& kernel, KWorkerTask* task);
std::jthread m_waiting_thread;
std::mutex m_task_mutex; private:
std::condition_variable m_task_cv; Common::ThreadWorker m_waiting_thread;
std::vector<KWorkerTask*> m_task_queue;
}; };
} // namespace Kernel } // namespace Kernel
+11 -14
View File
@@ -967,16 +967,12 @@ Kernel::KHardwareTimer& KernelCore::HardwareTimer() {
return *impl->hardware_timer; return *impl->hardware_timer;
} }
KAutoObjectWithListContainer* KernelCore::ObjectListContainer() { KAutoObjectWithListContainer& KernelCore::ObjectListContainer() {
if (!impl->global_object_list_container) return *impl->global_object_list_container;
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
const KAutoObjectWithListContainer* KernelCore::ObjectListContainer() const { const KAutoObjectWithListContainer& KernelCore::ObjectListContainer() const {
if (!impl->global_object_list_container) return *impl->global_object_list_container;
return nullptr;
return std::addressof(*impl->global_object_list_container);
} }
void KernelCore::PrepareReschedule(std::size_t id) { void KernelCore::PrepareReschedule(std::size_t id) {
@@ -1005,10 +1001,16 @@ void KernelCore::UnregisterInUseObject(KAutoObject* object) {
void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) { void KernelCore::RunServer(std::unique_ptr<Service::ServerManager>&& server_manager) {
auto* manager = server_manager.get(); auto* manager = server_manager.get();
if (!impl->is_shutting_down) {
{
std::scoped_lock lk{impl->server_lock}; std::scoped_lock lk{impl->server_lock};
if (impl->is_shutting_down) {
return;
}
impl->server_managers.emplace_back(std::move(server_manager)); impl->server_managers.emplace_back(std::move(server_manager));
} }
manager->LoopProcess(); manager->LoopProcess();
} }
@@ -1253,11 +1255,6 @@ void KernelCore::SuspendEmulation(bool suspended) {
} }
void KernelCore::ShutdownCores() { void KernelCore::ShutdownCores() {
// Notify shutdown (pre-emptively)
for (auto& sm : impl->server_managers) {
sm->NotifyShutdown();
}
impl->TerminateAllProcesses(); impl->TerminateAllProcesses();
KScopedSchedulerLock lk{*this}; KScopedSchedulerLock lk{*this};
for (auto* thread : impl->shutdown_threads) for (auto* thread : impl->shutdown_threads)
+3 -2
View File
@@ -175,8 +175,9 @@ public:
/// Stops execution of 'id' core, in order to reschedule a new thread. /// Stops execution of 'id' core, in order to reschedule a new thread.
void PrepareReschedule(std::size_t id); void PrepareReschedule(std::size_t id);
KAutoObjectWithListContainer* ObjectListContainer(); KAutoObjectWithListContainer& ObjectListContainer();
const KAutoObjectWithListContainer* ObjectListContainer() const;
const KAutoObjectWithListContainer& ObjectListContainer() const;
/// Registers all kernel objects with the global emulation state, this is purely for tracking /// Registers all kernel objects with the global emulation state, this is purely for tracking
/// leaks after emulation has been shutdown. /// leaks after emulation has been shutdown.
+3 -4
View File
@@ -151,8 +151,7 @@ public:
const bool is_initialized = this->IsInitialized(); const bool is_initialized = this->IsInitialized();
uintptr_t arg = 0; uintptr_t arg = 0;
if (is_initialized) { if (is_initialized) {
if (auto const olc = kernel.ObjectListContainer(); olc) kernel.ObjectListContainer().Unregister(this);
olc->Unregister(this);
arg = this->GetPostDestroyArgument(); arg = this->GetPostDestroyArgument();
this->Finalize(kernel); this->Finalize(kernel);
} }
@@ -176,7 +175,7 @@ public:
public: public:
static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) { static void InitializeSlabHeap(KernelCore& kernel, void* memory, size_t memory_size) {
kernel.SlabHeap<Derived>().Initialize(memory, memory_size); kernel.SlabHeap<Derived>().Initialize(memory, memory_size);
kernel.ObjectListContainer()->Initialize(); kernel.ObjectListContainer().Initialize();
} }
static Derived* Create(KernelCore& kernel) { static Derived* Create(KernelCore& kernel) {
@@ -187,7 +186,7 @@ public:
} }
static void Register(KernelCore& kernel, Derived* obj) { static void Register(KernelCore& kernel, Derived* obj) {
return kernel.ObjectListContainer()->Register(obj); return kernel.ObjectListContainer().Register(obj);
} }
static size_t GetObjectSize(KernelCore& kernel) { static size_t GetObjectSize(KernelCore& kernel) {
+5 -4
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
@@ -90,9 +90,10 @@ static Service::PSC::Time::LocationName GetTimeZoneString(
} }
TimeManager::TimeManager(Core::System& system) TimeManager::TimeManager(Core::System& system)
: m_steady_clock_resource{system}, m_time_zone_binary{system} : m_steady_clock_resource{system}, m_time_zone_binary{system}, m_worker{
, m_worker{system, m_steady_clock_resource, m_file_timestamp_worker} system,
{ m_steady_clock_resource,
m_file_timestamp_worker} {
m_time_m = m_time_m =
system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true); system.ServiceManager().GetService<Service::PSC::Time::ServiceManager>("time:m", true);
+168 -169
View File
@@ -20,17 +20,15 @@
namespace Service::Glue::Time { namespace Service::Glue::Time {
TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource, FileTimestampWorker& file_timestamp_worker) TimeWorker::TimeWorker(Core::System& system, StandardSteadyClockResource& steady_clock_resource,
: m_system{system} FileTimestampWorker& file_timestamp_worker)
, m_ctx{m_system, "Glue:TimeWorker"} : m_system{system}, m_ctx{m_system, "Glue:TimeWorker"}, m_event{m_ctx.CreateEvent(
, m_event{m_ctx.CreateEvent("Glue:TimeWorker:Event")} "Glue:TimeWorker:Event")},
, m_steady_clock_resource{steady_clock_resource} m_steady_clock_resource{steady_clock_resource},
, m_file_timestamp_worker{file_timestamp_worker} m_file_timestamp_worker{file_timestamp_worker}, m_timer_steady_clock{m_ctx.CreateEvent(
, m_timer_steady_clock{m_ctx.CreateEvent("Glue:TimeWorker:SteadyClockTimerEvent")} "Glue:TimeWorker:SteadyClockTimerEvent")},
, m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")} m_timer_file_system{m_ctx.CreateEvent("Glue:TimeWorker:FileTimeTimerEvent")},
, m_alarm_worker{m_system, m_steady_clock_resource} m_alarm_worker{m_system, m_steady_clock_resource}, m_pm_state_change_handler{m_alarm_worker} {
, m_pm_state_change_handler{m_alarm_worker}
{
m_timer_steady_clock_timing_event = Core::Timing::CreateEvent( m_timer_steady_clock_timing_event = Core::Timing::CreateEvent(
"Time::SteadyClockEvent", "Time::SteadyClockEvent",
[this](s64 time, [this](s64 time,
@@ -52,12 +50,11 @@ TimeWorker::~TimeWorker() {
m_local_clock_event->Signal(m_system.Kernel()); m_local_clock_event->Signal(m_system.Kernel());
m_network_clock_event->Signal(m_system.Kernel()); m_network_clock_event->Signal(m_system.Kernel());
m_ephemeral_clock_event->Signal(m_system.Kernel()); m_ephemeral_clock_event->Signal(m_system.Kernel());
std::this_thread::sleep_for(std::chrono::milliseconds(16));
if (m_thread.joinable()) { m_thread.request_stop();
m_thread.request_stop(); m_event->Signal(m_system.Kernel());
m_event->Signal(m_system.Kernel()); m_thread.join();
m_thread.join();
}
m_ctx.CloseEvent(m_event); m_ctx.CloseEvent(m_event);
m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event); m_system.CoreTiming().UnscheduleEvent(m_timer_steady_clock_timing_event);
@@ -125,164 +122,166 @@ void TimeWorker::Initialize(std::shared_ptr<Service::PSC::Time::StaticService> t
} }
void TimeWorker::StartThread() { void TimeWorker::StartThread() {
m_thread = m_system.Kernel().RunOnHostCoreThread("TimeWorker", [this]() { m_thread = std::jthread(std::bind_front(&TimeWorker::ThreadFunc, this));
auto const stop_token = m_thread.get_stop_token(); }
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
while (!stop_token.stop_requested()) {
enum class EventType : s32 {
Exit = 0,
PowerStateChange = 1,
SignalAlarms = 2,
UpdateLocalSystemClock = 3,
UpdateNetworkSystemClock = 4,
UpdateEphemeralSystemClock = 5,
UpdateSteadyClock = 6,
UpdateFileTimestamp = 7,
AutoCorrect = 8,
};
s32 index{}; void TimeWorker::ThreadFunc(std::stop_token stop_token) {
Common::SetCurrentThreadName("TimeWorker");
Common::SetCurrentThreadPriority(Common::ThreadPriority::Low);
if (m_pm_state_change_handler.m_priority != 0) { while (!stop_token.stop_requested()) {
// TODO: gIPmModuleService::GetEvent() 1 enum class EventType : s32 {
index = WaitAny( Exit = 0,
m_system.Kernel(), PowerStateChange = 1,
&m_event->GetReadableEvent(), // 0 SignalAlarms = 2,
&m_alarm_worker.GetEvent() // 1 UpdateLocalSystemClock = 3,
); UpdateNetworkSystemClock = 4,
} else { UpdateEphemeralSystemClock = 5,
// TODO: gIPmModuleService::GetEvent() 1 UpdateSteadyClock = 6,
index = WaitAny( UpdateFileTimestamp = 7,
m_system.Kernel(), AutoCorrect = 8,
&m_event->GetReadableEvent(), // 0 };
&m_alarm_worker.GetEvent(), // 1
&m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_local_clock_event, // 3
m_network_clock_event, // 4
m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
&m_timer_file_system->GetReadableEvent(), // 7
m_standard_user_auto_correct_clock_event // 8
);
}
if (stop_token.stop_requested()) s32 index{};
break;
switch (EventType(index)) { if (m_pm_state_change_handler.m_priority != 0) {
case EventType::Exit: // TODO: gIPmModuleService::GetEvent() 1
return; index = WaitAny(m_system.Kernel(),
&m_event->GetReadableEvent(), // 0
case EventType::PowerStateChange: &m_alarm_worker.GetEvent() // 1
m_alarm_worker.GetEvent().Clear(m_system.Kernel()); );
if (m_pm_state_change_handler.m_priority <= 1) { } else {
m_alarm_worker.OnPowerStateChanged(); // TODO: gIPmModuleService::GetEvent() 1
} index = WaitAny(m_system.Kernel(),
break; &m_event->GetReadableEvent(), // 0
&m_alarm_worker.GetEvent(), // 1
case EventType::SignalAlarms: &m_alarm_worker.GetTimerEvent().GetReadableEvent(), // 2
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel()); m_local_clock_event, // 3
m_time_m->CheckAndSignalAlarms(); m_network_clock_event, // 4
break; m_ephemeral_clock_event, // 5
&m_timer_steady_clock->GetReadableEvent(), // 6
case EventType::UpdateLocalSystemClock: { &m_timer_file_system->GetReadableEvent(), // 7
m_local_clock_event->Clear(m_system.Kernel()); m_standard_user_auto_correct_clock_event // 8
);
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
// TODO system report "standard_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_network_clock_context = context;
if (!m_ig_report_network_clock_context_set) {
m_ig_report_network_clock_context_set = true;
}
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
if (res != ResultSuccess) {
break;
}
s64 time{};
res = m_ephemeral_clock->GetCurrentTime(&time);
if (res != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
? m_report_ephemeral_clock_context.offset
: 0};
// TODO system report "ephemeral_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_ephemeral_clock_context = context;
if (!m_ig_report_ephemeral_clock_context_set) {
m_ig_report_ephemeral_clock_context_set = true;
}
break;
}
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear(m_system.Kernel());
m_steady_clock_resource.UpdateTime();
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
break;
case EventType::UpdateFileTimestamp:
m_timer_file_system->Clear(m_system.Kernel());
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
case EventType::AutoCorrect: {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break;
}
default:
UNREACHABLE();
}
} }
});
switch (static_cast<EventType>(index)) {
case EventType::Exit:
return;
case EventType::PowerStateChange:
m_alarm_worker.GetEvent().Clear(m_system.Kernel());
if (m_pm_state_change_handler.m_priority <= 1) {
m_alarm_worker.OnPowerStateChanged();
}
break;
case EventType::SignalAlarms:
m_alarm_worker.GetTimerEvent().Clear(m_system.Kernel());
m_time_m->CheckAndSignalAlarms();
break;
case EventType::UpdateLocalSystemClock: {
m_local_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_local_clock->GetSystemClockContext(&context));
m_set_sys->SetUserSystemClockContext(context);
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateNetworkSystemClock: {
m_network_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
R_ASSERT(m_network_clock->GetSystemClockContext(&context));
m_set_sys->SetNetworkSystemClockContext(context);
s64 time{};
if (m_network_clock->GetCurrentTime(&time) != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{
m_ig_report_network_clock_context_set ? m_report_network_clock_context.offset : 0};
// TODO system report "standard_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_network_clock_context = context;
if (!m_ig_report_network_clock_context_set) {
m_ig_report_network_clock_context_set = true;
}
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
}
case EventType::UpdateEphemeralSystemClock: {
m_ephemeral_clock_event->Clear(m_system.Kernel());
Service::PSC::Time::SystemClockContext context{};
auto res = m_ephemeral_clock->GetSystemClockContext(&context);
if (res != ResultSuccess) {
break;
}
s64 time{};
res = m_ephemeral_clock->GetCurrentTime(&time);
if (res != ResultSuccess) {
break;
}
[[maybe_unused]] auto offset_before{m_ig_report_ephemeral_clock_context_set
? m_report_ephemeral_clock_context.offset
: 0};
// TODO system report "ephemeral_netclock_operation"
// "clock_time" = time
// "context_offset_before" = offset_before
// "context_offset_after" = context.offset
m_report_ephemeral_clock_context = context;
if (!m_ig_report_ephemeral_clock_context_set) {
m_ig_report_ephemeral_clock_context_set = true;
}
break;
}
case EventType::UpdateSteadyClock:
m_timer_steady_clock->Clear(m_system.Kernel());
m_steady_clock_resource.UpdateTime();
m_time_m->SetStandardSteadyClockBaseTime(m_steady_clock_resource.GetTime());
break;
case EventType::UpdateFileTimestamp:
m_timer_file_system->Clear(m_system.Kernel());
m_file_timestamp_worker.SetFilesystemPosixTime();
break;
case EventType::AutoCorrect: {
m_standard_user_auto_correct_clock_event->Clear(m_system.Kernel());
bool automatic_correction{};
R_ASSERT(m_time_sm->IsStandardUserSystemClockAutomaticCorrectionEnabled(
&automatic_correction));
Service::PSC::Time::SteadyClockTimePoint time_point{};
R_ASSERT(
m_time_sm->GetStandardUserSystemClockAutomaticCorrectionUpdatedTime(&time_point));
m_set_sys->SetUserSystemClockAutomaticCorrectionEnabled(automatic_correction);
m_set_sys->SetUserSystemClockAutomaticCorrectionUpdatedTime(time_point);
break;
}
default:
UNREACHABLE();
}
}
} }
} // namespace Service::Glue::Time } // namespace Service::Glue::Time
+1 -3
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -36,6 +33,7 @@ public:
void StartThread(); void StartThread();
private:
template <typename T> template <typename T>
T GetSettingsItemValue(const std::string& category, const std::string& name); T GetSettingsItemValue(const std::string& category, const std::string& name);
+2 -8
View File
@@ -253,13 +253,6 @@ void ServerManager::StartAdditionalHostThreads(const char* name, size_t num_thre
} }
} }
/// @brief Notifies that the system is shutting down (pre-emptively terminate threads)
void ServerManager::NotifyShutdown() {
m_stop_source.request_stop();
// Wake them up regardless
m_wakeup_event->Signal(m_system.Kernel());
}
Result ServerManager::LoopProcess() { Result ServerManager::LoopProcess() {
SCOPE_EXIT { SCOPE_EXIT {
m_stopped.Set(); m_stopped.Set();
@@ -292,8 +285,9 @@ MultiWaitHolder* ServerManager::WaitSignaled() {
this->LinkDeferred(); this->LinkDeferred();
// If we're done, return before we start waiting. // If we're done, return before we start waiting.
if (m_stop_source.stop_requested()) if (m_stop_source.stop_requested()) {
return nullptr; return nullptr;
}
auto* selected = m_multi_wait.WaitAny(m_system.Kernel()); auto* selected = m_multi_wait.WaitAny(m_system.Kernel());
if (selected == std::addressof(*m_wakeup_holder)) { if (selected == std::addressof(*m_wakeup_holder)) {
-4
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
@@ -52,7 +49,6 @@ public:
Result LoopProcess(); Result LoopProcess();
void StartAdditionalHostThreads(const char* name, size_t num_threads); void StartAdditionalHostThreads(const char* name, size_t num_threads);
void NotifyShutdown();
static void RunServer(std::unique_ptr<ServerManager>&& server); static void RunServer(std::unique_ptr<ServerManager>&& server);
+1 -3
View File
@@ -94,9 +94,6 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"ldn", &LDN::LoopProcess}, {"ldn", &LDN::LoopProcess},
{"nvservices", &Nvidia::LoopProcess}, {"nvservices", &Nvidia::LoopProcess},
{"bsdsocket", &Sockets::LoopProcess}, {"bsdsocket", &Sockets::LoopProcess},
// TODO: glue is only required here as otherwise the dummythread doesn't close
// TODO: rework sched so this isn't needed
{"glue", &Glue::LoopProcess},
}) })
kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach(); kernel.RunOnHostCoreProcess(std::string(e.first), [&system, f = e.second] { f(system); }).detach();
kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach(); kernel.RunOnHostCoreProcess("vi", [&, token] { VI::LoopProcess(system, token); }).detach();
@@ -120,6 +117,7 @@ Services::Services(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system
{"friends", &Friend::LoopProcess}, {"friends", &Friend::LoopProcess},
{"settings", &Set::LoopProcess}, {"settings", &Set::LoopProcess},
{"psc", &PSC::LoopProcess}, {"psc", &PSC::LoopProcess},
{"glue", &Glue::LoopProcess},
{"grc", &GRC::LoopProcess}, {"grc", &GRC::LoopProcess},
{"hid", &HID::LoopProcess}, {"hid", &HID::LoopProcess},
{"jit", &JIT::LoopProcess}, {"jit", &JIT::LoopProcess},
+19 -15
View File
@@ -11,7 +11,6 @@
#include "common/thread.h" #include "common/thread.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_timing.h" #include "core/core_timing.h"
#include "core/hle/kernel/kernel.h"
#include "core/hle/service/vi/conductor.h" #include "core/hle/service/vi/conductor.h"
#include "core/hle/service/vi/container.h" #include "core/hle/service/vi/container.h"
#include "core/hle/service/vi/display_list.h" #include "core/hle/service/vi/display_list.h"
@@ -39,18 +38,7 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
}); });
system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event); system.CoreTiming().ScheduleLoopingEvent(FrameNs, FrameNs, m_event);
m_thread = system.Kernel().RunOnHostCoreThread("VSyncThread", [this]() { m_thread = std::jthread([this](std::stop_token token) { this->VsyncThread(token); });
auto const stop_token = m_thread.get_stop_token();
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!stop_token.stop_requested()) {
m_signal.Wait();
if (stop_token.stop_requested() || m_system.IsShuttingDown()) {
break;
}
this->ProcessVsync();
}
});
} else { } else {
m_event = Core::Timing::CreateEvent( m_event = Core::Timing::CreateEvent(
"ScreenComposition", "ScreenComposition",
@@ -66,10 +54,10 @@ Conductor::Conductor(Core::System& system, Container& container, DisplayList& di
Conductor::~Conductor() { Conductor::~Conductor() {
m_system.CoreTiming().UnscheduleEvent(m_event); m_system.CoreTiming().UnscheduleEvent(m_event);
if (m_thread.joinable()) {
if (m_system.IsMulticore()) {
m_thread.request_stop(); m_thread.request_stop();
m_signal.Set(); m_signal.Set();
m_thread.join();
} }
} }
@@ -95,6 +83,22 @@ void Conductor::ProcessVsync() {
} }
} }
void Conductor::VsyncThread(std::stop_token token) {
Common::SetCurrentThreadName("VSyncThread");
Common::SetCurrentThreadPriority(Common::ThreadPriority::VeryHigh);
Common::SetCurrentThreadToPerformanceCores();
while (!token.stop_requested()) {
m_signal.Wait();
if (m_system.IsShuttingDown()) {
return;
}
this->ProcessVsync();
}
}
s64 Conductor::GetNextTicks() const { s64 Conductor::GetNextTicks() const {
const auto& settings = Settings::values; const auto& settings = Settings::values;
auto speed_scale = 1.f; auto speed_scale = 1.f;
+1 -18
View File
@@ -1,15 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
// gcc14 bug
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
#include "common/assert.h" #include "common/assert.h"
#include "common/scope_exit.h" #include "common/scope_exit.h"
#include "core/memory/dmnt_cheat_types.h" #include "core/memory/dmnt_cheat_types.h"
@@ -232,10 +223,7 @@ DmntCheatVm::Callbacks::~Callbacks() = default;
bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) { bool DmntCheatVm::DecodeNextOpcode(CheatVmOpcode& out) {
// If we've ever seen a decode failure, return false. // If we've ever seen a decode failure, return false.
bool valid = decode_success; bool valid = decode_success;
CheatVmOpcode opcode = { CheatVmOpcode opcode = {};
.begin_conditional_block = false,
.opcode = {}
};
SCOPE_EXIT { SCOPE_EXIT {
decode_success &= valid; decode_success &= valid;
if (valid) { if (valid) {
@@ -1278,8 +1266,3 @@ void DmntCheatVm::Execute(const CheatProcessMetadata& metadata) {
} }
} // namespace Core::Memory } // namespace Core::Memory
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
+9 -11
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -254,16 +254,14 @@ struct UnrecognizedInstruction {
struct CheatVmOpcode { struct CheatVmOpcode {
bool begin_conditional_block{}; bool begin_conditional_block{};
std::variant< std::variant<StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode,
std::monostate, LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode,
StoreStaticOpcode, BeginConditionalOpcode, EndConditionalOpcode, ControlLoopOpcode, PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode,
LoadRegisterStaticOpcode, LoadRegisterMemoryOpcode, StoreStaticToAddressOpcode, PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode,
PerformArithmeticStaticOpcode, BeginKeypressConditionalOpcode, BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode,
PerformArithmeticRegisterOpcode, StoreRegisterToAddressOpcode, SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode,
BeginRegisterConditionalOpcode, SaveRestoreRegisterOpcode, ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction>
SaveRestoreRegisterMaskOpcode, ReadWriteStaticRegisterOpcode, PauseProcessOpcode, opcode{};
ResumeProcessOpcode, DebugLogOpcode, UnrecognizedInstruction
> opcode{};
}; };
class DmntCheatVm { class DmntCheatVm {
@@ -170,19 +170,13 @@ template<>
// check for marked bit, use as unmapped if marked // check for marked bit, use as unmapped if marked
if (ctx.conf.page_table_marked_bit) { if (ctx.conf.page_table_marked_bit) {
auto const marked_bit = *ctx.conf.page_table_marked_bit; code.bt(page, *ctx.conf.page_table_marked_bit);
if (s64(s32(1 << marked_bit)) == s64(1 << marked_bit)) { code.jc(abort, code.T_NEAR);
code.test(page, s32(1 << marked_bit));
code.jnz(abort, code.T_NEAR);
} else {
code.bt(page, marked_bit);
code.jc(abort, code.T_NEAR);
}
} }
// mask away attributes // mask away attributes
if (ctx.conf.page_table_pointer_mask == 0) { if (ctx.conf.page_table_pointer_mask == 0) {
code.test(page, page); code.test(page, page);
} else if (s64(s32(ctx.conf.page_table_pointer_mask)) == s64(ctx.conf.page_table_pointer_mask)) { } else if (std::in_range<s32>(ctx.conf.page_table_pointer_mask)) {
code.and_(page, ctx.conf.page_table_pointer_mask); code.and_(page, ctx.conf.page_table_pointer_mask);
} else { } else {
code.mov(tmp, ctx.conf.page_table_pointer_mask); code.mov(tmp, ctx.conf.page_table_pointer_mask);
@@ -79,10 +79,8 @@ void NpadAbstractButtonHandler::UpdateAllButtonLifo() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (auto const shfmt = data->shared_memory_format; shfmt) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; UpdateButtonLifo(npad_entry, data->aruid);
UpdateButtonLifo(npad_entry, data->aruid);
}
} }
} }
@@ -90,19 +88,19 @@ void NpadAbstractButtonHandler::UpdateCoreBatteryState() {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
for (std::size_t i = 0; i < AruidIndexMax; i++) { for (std::size_t i = 0; i < AruidIndexMax; i++) {
auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i); auto* data = applet_resource_holder->applet_resource->GetAruidDataByIndex(i);
if (auto const shfmt = data->shared_memory_format; shfmt) { auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
auto& npad_entry = shfmt->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; UpdateButtonLifo(npad_entry, data->aruid);
UpdateButtonLifo(npad_entry, data->aruid);
}
} }
} }
void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) { void NpadAbstractButtonHandler::UpdateButtonState(u64 aruid) {
Core::HID::NpadIdType npad_id = properties_handler->GetNpadId(); Core::HID::NpadIdType npad_id = properties_handler->GetNpadId();
if (auto data = applet_resource_holder->applet_resource->GetAruidData(aruid); data) { auto* data = applet_resource_holder->applet_resource->GetAruidData(aruid);
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)]; if (data == nullptr) {
UpdateButtonLifo(npad_entry, aruid); return;
} }
auto& npad_entry = data->shared_memory_format->npad.npad_entry[NpadIdTypeToIndex(npad_id)];
UpdateButtonLifo(npad_entry, aruid);
} }
Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) { Result NpadAbstractButtonHandler::SetHomeProtection(bool is_enabled, u64 aruid) {
@@ -496,30 +496,31 @@ void SetupCapabilities(const Profile& profile, const Info& info, EmitContext& ct
} }
void PatchPhiNodes(IR::Program& program, EmitContext& ctx) { void PatchPhiNodes(IR::Program& program, EmitContext& ctx) {
// Flatten all leading PHIs from each block into a vector // Flatten all leading PHIs from each block into a vector
std::vector<IR::Inst*> phi_instructions; std::vector<IR::Inst*> phi_instructions;
for (IR::Block* block : program.blocks) { for (IR::Block* block : program.blocks) {
for (auto it = block->begin(); it != block->end(); ++it) { for (auto it = block->begin(); it != block->end(); ++it) {
if (it->GetOpcode() != IR::Opcode::Phi) if (it->GetOpcode() != IR::Opcode::Phi)
break; break;
phi_instructions.push_back(&*it); phi_instructions.push_back(&*it);
} }
} }
if (phi_instructions.empty()) { if (phi_instructions.empty()) {
return; // nothing to patch return; // nothing to patch
} }
// Start "before" first PHI; advance on phi_arg == 0 // Start "before" first PHI; advance on phi_arg == 0
size_t phi_index = size_t(-1); size_t phi_index = static_cast<size_t>(-1);
ctx.PatchDeferredPhi([&ctx, &phi_index, phi_insts = std::move(phi_instructions)](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
if (phi_arg == 0) { ctx.PatchDeferredPhi([&](size_t phi_arg, Id parent) -> std::pair<Id, Id> {
++phi_index; if (phi_arg == 0) {
++phi_index;
}
IR::Inst* phi = phi_instructions[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
} }
IR::Inst* phi = phi_insts[phi_index];
return { ctx.Def(phi->Arg(phi_arg)), parent };
});
}
} // Anonymous namespace } // Anonymous namespace
std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) { std::vector<u32> EmitSPIRV(const Profile& profile, const RuntimeInfo& runtime_info, IR::Program& program, Bindings& bindings) {