Add random sleep to game main thread on first boot when using LLE modules (#7199)
* Add random delay to app main thread * Suggestions * Remove randomness, only delay with lle * Apply suggestions * Fix clang format * Fix compilation (again) * Remove unused include
This commit is contained in:
		@@ -452,6 +452,7 @@ void Config::ReadCoreValues() {
 | 
			
		||||
 | 
			
		||||
    if (global) {
 | 
			
		||||
        ReadBasicSetting(Settings::values.use_cpu_jit);
 | 
			
		||||
        ReadBasicSetting(Settings::values.delay_start_for_lle_modules);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
@@ -979,6 +980,7 @@ void Config::SaveCoreValues() {
 | 
			
		||||
 | 
			
		||||
    if (global) {
 | 
			
		||||
        WriteBasicSetting(Settings::values.use_cpu_jit);
 | 
			
		||||
        WriteBasicSetting(Settings::values.delay_start_for_lle_modules);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    qt_config->endGroup();
 | 
			
		||||
 
 | 
			
		||||
@@ -94,6 +94,8 @@ void ConfigureDebug::SetConfiguration() {
 | 
			
		||||
    ui->toggle_console->setChecked(UISettings::values.show_console.GetValue());
 | 
			
		||||
    ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter.GetValue()));
 | 
			
		||||
    ui->toggle_cpu_jit->setChecked(Settings::values.use_cpu_jit.GetValue());
 | 
			
		||||
    ui->delay_start_for_lle_modules->setChecked(
 | 
			
		||||
        Settings::values.delay_start_for_lle_modules.GetValue());
 | 
			
		||||
    ui->toggle_renderer_debug->setChecked(Settings::values.renderer_debug.GetValue());
 | 
			
		||||
    ui->toggle_dump_command_buffers->setChecked(Settings::values.dump_command_buffers.GetValue());
 | 
			
		||||
 | 
			
		||||
@@ -125,6 +127,7 @@ void ConfigureDebug::ApplyConfiguration() {
 | 
			
		||||
    filter.ParseFilterString(Settings::values.log_filter.GetValue());
 | 
			
		||||
    Common::Log::SetGlobalFilter(filter);
 | 
			
		||||
    Settings::values.use_cpu_jit = ui->toggle_cpu_jit->isChecked();
 | 
			
		||||
    Settings::values.delay_start_for_lle_modules = ui->delay_start_for_lle_modules->isChecked();
 | 
			
		||||
    Settings::values.renderer_debug = ui->toggle_renderer_debug->isChecked();
 | 
			
		||||
    Settings::values.dump_command_buffers = ui->toggle_dump_command_buffers->isChecked();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -219,6 +219,25 @@
 | 
			
		||||
     </layout>
 | 
			
		||||
    </widget>
 | 
			
		||||
   </item>
 | 
			
		||||
    <item>
 | 
			
		||||
      <widget class="QGroupBox" name="groupBox_5">
 | 
			
		||||
        <property name="title">
 | 
			
		||||
          <string>Miscellaneus</string>
 | 
			
		||||
        </property>
 | 
			
		||||
        <layout class="QGridLayout" name="gridLayout_2">
 | 
			
		||||
          <item row="1" column="0">
 | 
			
		||||
            <widget class="QCheckBox" name="delay_start_for_lle_modules">
 | 
			
		||||
              <property name="toolTip">
 | 
			
		||||
                <string><html><head/><body><p>Introduces a delay to the first ever launched app thread if LLE modules are enabled, to allow them to initialize.</p></body></html></string>
 | 
			
		||||
              </property>
 | 
			
		||||
              <property name="text">
 | 
			
		||||
                <string>Delay app start for LLE module initialization</string>
 | 
			
		||||
              </property>
 | 
			
		||||
            </widget>
 | 
			
		||||
          </item>
 | 
			
		||||
        </layout>
 | 
			
		||||
      </widget>
 | 
			
		||||
    </item>
 | 
			
		||||
   <item>
 | 
			
		||||
    <widget class="QLabel" name="label_cpu_clock_info">
 | 
			
		||||
     <property name="text">
 | 
			
		||||
 
 | 
			
		||||
@@ -141,6 +141,7 @@ void LogSettings() {
 | 
			
		||||
    log_setting("System_RegionValue", values.region_value.GetValue());
 | 
			
		||||
    log_setting("System_PluginLoader", values.plugin_loader_enabled.GetValue());
 | 
			
		||||
    log_setting("System_PluginLoaderAllowed", values.allow_plugin_loader.GetValue());
 | 
			
		||||
    log_setting("Debugging_DelayStartForLLEModules", values.delay_start_for_lle_modules.GetValue());
 | 
			
		||||
    log_setting("Debugging_UseGdbstub", values.use_gdbstub.GetValue());
 | 
			
		||||
    log_setting("Debugging_GdbstubPort", values.gdbstub_port.GetValue());
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -529,6 +529,7 @@ struct Values {
 | 
			
		||||
    // Debugging
 | 
			
		||||
    bool record_frame_times;
 | 
			
		||||
    std::unordered_map<std::string, bool> lle_modules;
 | 
			
		||||
    Setting<bool> delay_start_for_lle_modules{true, "delay_start_for_lle_modules"};
 | 
			
		||||
    Setting<bool> use_gdbstub{false, "use_gdbstub"};
 | 
			
		||||
    Setting<u16> gdbstub_port{24689, "gdbstub_port"};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -183,6 +183,7 @@ void KernelSystem::serialize(Archive& ar, const unsigned int file_version) {
 | 
			
		||||
    ar& next_thread_id;
 | 
			
		||||
    ar& memory_mode;
 | 
			
		||||
    ar& n3ds_hw_caps;
 | 
			
		||||
    ar& main_thread_extended_sleep;
 | 
			
		||||
    // Deliberately don't include debugger info to allow debugging through loads
 | 
			
		||||
 | 
			
		||||
    if (Archive::is_loading::value) {
 | 
			
		||||
 
 | 
			
		||||
@@ -185,12 +185,14 @@ public:
 | 
			
		||||
     * @param processor_id The ID(s) of the processors on which the thread is desired to be run
 | 
			
		||||
     * @param stack_top The address of the thread's stack top
 | 
			
		||||
     * @param owner_process The parent process for the thread
 | 
			
		||||
     * @param make_ready If the thread should be put in the ready queue
 | 
			
		||||
     * @return A shared pointer to the newly created thread
 | 
			
		||||
     */
 | 
			
		||||
    ResultVal<std::shared_ptr<Thread>> CreateThread(std::string name, VAddr entry_point,
 | 
			
		||||
                                                    u32 priority, u32 arg, s32 processor_id,
 | 
			
		||||
                                                    VAddr stack_top,
 | 
			
		||||
                                                    std::shared_ptr<Process> owner_process);
 | 
			
		||||
                                                    std::shared_ptr<Process> owner_process,
 | 
			
		||||
                                                    bool make_ready = true);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Creates a semaphore.
 | 
			
		||||
@@ -338,6 +340,15 @@ public:
 | 
			
		||||
 | 
			
		||||
    Core::Timing& timing;
 | 
			
		||||
 | 
			
		||||
    /// Sleep main thread of the first ever launched non-sysmodule process.
 | 
			
		||||
    void SetAppMainThreadExtendedSleep(bool requires_sleep) {
 | 
			
		||||
        main_thread_extended_sleep = requires_sleep;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool GetAppMainThreadExtendedSleep() const {
 | 
			
		||||
        return main_thread_extended_sleep;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    void MemoryInit(MemoryMode memory_mode, New3dsMemoryMode n3ds_mode, u64 override_init_time);
 | 
			
		||||
 | 
			
		||||
@@ -386,6 +397,14 @@ private:
 | 
			
		||||
     */
 | 
			
		||||
    std::recursive_mutex hle_lock;
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * Flags non system module main threads to wait a bit before running. On real hardware,
 | 
			
		||||
     * system modules have plenty of time to load before the game is loaded, but on citra they
 | 
			
		||||
     * start at the same time as the game. The artificial wait gives system modules some time
 | 
			
		||||
     * to load and setup themselves before the game starts.
 | 
			
		||||
     */
 | 
			
		||||
    bool main_thread_extended_sleep = false;
 | 
			
		||||
 | 
			
		||||
    friend class boost::serialization::access;
 | 
			
		||||
    template <class Archive>
 | 
			
		||||
    void serialize(Archive& ar, const unsigned int file_version);
 | 
			
		||||
 
 | 
			
		||||
@@ -10,8 +10,10 @@
 | 
			
		||||
#include "common/common_types.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "common/serialization/boost_flat_set.h"
 | 
			
		||||
#include "common/settings.h"
 | 
			
		||||
#include "core/arm/arm_interface.h"
 | 
			
		||||
#include "core/arm/skyeye_common/armstate.h"
 | 
			
		||||
#include "core/core.h"
 | 
			
		||||
#include "core/hle/kernel/errors.h"
 | 
			
		||||
#include "core/hle/kernel/kernel.h"
 | 
			
		||||
#include "core/hle/kernel/mutex.h"
 | 
			
		||||
@@ -324,7 +326,7 @@ static void ResetThreadContext(Core::ARM_Interface::ThreadContext& context, u32
 | 
			
		||||
 | 
			
		||||
ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
 | 
			
		||||
    std::string name, VAddr entry_point, u32 priority, u32 arg, s32 processor_id, VAddr stack_top,
 | 
			
		||||
    std::shared_ptr<Process> owner_process) {
 | 
			
		||||
    std::shared_ptr<Process> owner_process, bool make_ready) {
 | 
			
		||||
    // Check if priority is in ranged. Lowest priority -> highest priority id.
 | 
			
		||||
    if (priority > ThreadPrioLowest) {
 | 
			
		||||
        LOG_ERROR(Kernel_SVC, "Invalid thread priority: {}", priority);
 | 
			
		||||
@@ -367,8 +369,11 @@ ResultVal<std::shared_ptr<Thread>> KernelSystem::CreateThread(
 | 
			
		||||
    // to initialize the context
 | 
			
		||||
    ResetThreadContext(thread->context, stack_top, entry_point, arg);
 | 
			
		||||
 | 
			
		||||
    thread_managers[processor_id]->ready_queue.push_back(thread->current_priority, thread.get());
 | 
			
		||||
    thread->status = ThreadStatus::Ready;
 | 
			
		||||
    if (make_ready) {
 | 
			
		||||
        thread_managers[processor_id]->ready_queue.push_back(thread->current_priority,
 | 
			
		||||
                                                             thread.get());
 | 
			
		||||
        thread->status = ThreadStatus::Ready;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return thread;
 | 
			
		||||
}
 | 
			
		||||
@@ -405,16 +410,36 @@ void Thread::BoostPriority(u32 priority) {
 | 
			
		||||
 | 
			
		||||
std::shared_ptr<Thread> SetupMainThread(KernelSystem& kernel, u32 entry_point, u32 priority,
 | 
			
		||||
                                        std::shared_ptr<Process> owner_process) {
 | 
			
		||||
 | 
			
		||||
    constexpr s64 sleep_app_thread_ns = 2'600'000'000LL;
 | 
			
		||||
    constexpr u32 system_module_tid_high = 0x00040130;
 | 
			
		||||
 | 
			
		||||
    const bool is_lle_service =
 | 
			
		||||
        static_cast<u32>(owner_process->codeset->program_id >> 32) == system_module_tid_high;
 | 
			
		||||
 | 
			
		||||
    s64 sleep_time_ns = 0;
 | 
			
		||||
    if (!is_lle_service && kernel.GetAppMainThreadExtendedSleep()) {
 | 
			
		||||
        if (Settings::values.delay_start_for_lle_modules) {
 | 
			
		||||
            sleep_time_ns = sleep_app_thread_ns;
 | 
			
		||||
        }
 | 
			
		||||
        kernel.SetAppMainThreadExtendedSleep(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Initialize new "main" thread
 | 
			
		||||
    auto thread_res =
 | 
			
		||||
        kernel.CreateThread("main", entry_point, priority, 0, owner_process->ideal_processor,
 | 
			
		||||
                            Memory::HEAP_VADDR_END, owner_process);
 | 
			
		||||
                            Memory::HEAP_VADDR_END, owner_process, sleep_time_ns == 0);
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<Thread> thread = std::move(thread_res).Unwrap();
 | 
			
		||||
 | 
			
		||||
    thread->context.fpscr =
 | 
			
		||||
        FPSCR_DEFAULT_NAN | FPSCR_FLUSH_TO_ZERO | FPSCR_ROUND_TOZERO | FPSCR_IXC; // 0x03C00010
 | 
			
		||||
 | 
			
		||||
    if (sleep_time_ns != 0) {
 | 
			
		||||
        thread->status = ThreadStatus::WaitSleep;
 | 
			
		||||
        thread->WakeAfterDelay(sleep_time_ns);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Note: The newly created thread will be run when the scheduler fires.
 | 
			
		||||
    return thread;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -214,10 +214,20 @@ static bool AttemptLLE(const ServiceModuleInfo& service_module) {
 | 
			
		||||
/// Initialize ServiceManager
 | 
			
		||||
void Init(Core::System& core) {
 | 
			
		||||
    SM::ServiceManager::InstallInterfaces(core);
 | 
			
		||||
    core.Kernel().SetAppMainThreadExtendedSleep(false);
 | 
			
		||||
    bool lle_module_present = false;
 | 
			
		||||
 | 
			
		||||
    for (const auto& service_module : service_module_map) {
 | 
			
		||||
        if (!AttemptLLE(service_module) && service_module.init_function != nullptr)
 | 
			
		||||
        const bool has_lle = AttemptLLE(service_module);
 | 
			
		||||
        if (!has_lle && service_module.init_function != nullptr) {
 | 
			
		||||
            service_module.init_function(core);
 | 
			
		||||
        }
 | 
			
		||||
        lle_module_present |= has_lle;
 | 
			
		||||
    }
 | 
			
		||||
    if (lle_module_present) {
 | 
			
		||||
        // If there is at least one LLE module, tell the kernel to
 | 
			
		||||
        // add a extended sleep to the app main thread (if option enabled).
 | 
			
		||||
        core.Kernel().SetAppMainThreadExtendedSleep(true);
 | 
			
		||||
    }
 | 
			
		||||
    LOG_DEBUG(Service, "initialized OK");
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user