diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b32f273f..a576e36f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # OpenCore Legacy Patcher changelog +## 2.4.0 +- Reduce CPU usage on main UI thread + ## 2.3.2 - Resolve erroring in Passwords app and Safari Autofill on T1 Macs running 15.4 or later - Increment binaries: diff --git a/opencore_legacy_patcher/application_entry.py b/opencore_legacy_patcher/application_entry.py index 8617f5b2d..c1b7d454b 100644 --- a/opencore_legacy_patcher/application_entry.py +++ b/opencore_legacy_patcher/application_entry.py @@ -130,7 +130,7 @@ class OpenCoreLegacyPatcher: if not any(x in sys.argv for x in ignore_args): while self.constants.unpack_thread.is_alive(): - time.sleep(0.1) + time.sleep(self.constants.thread_sleep_interval) arguments.arguments(self.constants) diff --git a/opencore_legacy_patcher/constants.py b/opencore_legacy_patcher/constants.py index 550856877..eeaf9e909 100644 --- a/opencore_legacy_patcher/constants.py +++ b/opencore_legacy_patcher/constants.py @@ -155,6 +155,8 @@ class Constants: self.unpack_thread = None # Determine if unpack thread finished (threading.Thread) self.update_stage: int = 0 # Determine update stage (see gui_support.py) self.log_filepath: Path = None # Path to log file + self.thread_sleep_interval: float = 0.1 # Sleep interval between UI updates (seconds) - reduce refresh-rate to reduce CPU-usage + self.thread_nap_interval: float = 0.01 # Short Sleep interval between UI updates (seconds) - for faster UI updates of the progress bar self.commit_info: tuple = (None, None, None) # Commit info (Branch, Commit Date, Commit URL) diff --git a/opencore_legacy_patcher/wx_gui/gui_build.py b/opencore_legacy_patcher/wx_gui/gui_build.py index f673a2b80..796ba8c0b 100644 --- a/opencore_legacy_patcher/wx_gui/gui_build.py +++ b/opencore_legacy_patcher/wx_gui/gui_build.py @@ -6,6 +6,7 @@ import wx import logging import threading import traceback +import time from .. import constants @@ -101,12 +102,12 @@ class BuildFrame(wx.Frame): """ while gui_support.PayloadMount(self.constants, self).is_unpack_finished() is False: wx.Yield() + time.sleep(self.constants.thread_sleep_interval) thread = threading.Thread(target=self._build) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) self.return_button.Enable() diff --git a/opencore_legacy_patcher/wx_gui/gui_cache_os_update.py b/opencore_legacy_patcher/wx_gui/gui_cache_os_update.py index 5f6db193c..e3dfd0113 100644 --- a/opencore_legacy_patcher/wx_gui/gui_cache_os_update.py +++ b/opencore_legacy_patcher/wx_gui/gui_cache_os_update.py @@ -76,13 +76,11 @@ class OSUpdateFrame(wx.Frame): if results[HardwarePatchsetSettings.KERNEL_DEBUG_KIT_REQUIRED] is True: kdk_thread = threading.Thread(target=_kdk_thread_spawn) kdk_thread.start() - while kdk_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(kdk_thread) if results[HardwarePatchsetSettings.METALLIB_SUPPORT_PKG_REQUIRED] is True: metallib_thread = threading.Thread(target=_metallib_thread_spawn) metallib_thread.start() - while metallib_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(metallib_thread) download_objects = { @@ -149,8 +147,7 @@ class OSUpdateFrame(wx.Frame): kdk_checksum_thread = threading.Thread(target=_validate_kdk_checksum_thread) kdk_checksum_thread.start() - while kdk_checksum_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(kdk_checksum_thread) if self.kdk_checksum_result is False: logging.error("KDK checksum validation failed") @@ -172,8 +169,7 @@ class OSUpdateFrame(wx.Frame): kdk_install_thread = threading.Thread(target=_install_kdk_thread) kdk_install_thread.start() - while kdk_install_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(kdk_install_thread) if self.kdk_install_result is False: logging.info("Failed to install KDK") @@ -194,8 +190,7 @@ class OSUpdateFrame(wx.Frame): metallib_install_thread = threading.Thread(target=_install_metallib_thread) metallib_install_thread.start() - while metallib_install_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(metallib_install_thread) if self.metallib_install_result is False: logging.info("Failed to install Metallib") diff --git a/opencore_legacy_patcher/wx_gui/gui_download.py b/opencore_legacy_patcher/wx_gui/gui_download.py index aca36e9aa..6b34e578b 100644 --- a/opencore_legacy_patcher/wx_gui/gui_download.py +++ b/opencore_legacy_patcher/wx_gui/gui_download.py @@ -4,6 +4,7 @@ gui_download.py: Generate UI for downloading files import wx import logging +import time from .. import constants @@ -86,6 +87,7 @@ class DownloadFrame(wx.Frame): label_amount.Centre(wx.HORIZONTAL) wx.Yield() + time.sleep(self.constants.thread_sleep_interval) if self.download_obj.download_complete is False and self.user_cancelled is False: wx.MessageBox(f"Download failed: \n{self.download_obj.error_msg}", "Error", wx.OK | wx.ICON_ERROR) diff --git a/opencore_legacy_patcher/wx_gui/gui_install_oc.py b/opencore_legacy_patcher/wx_gui/gui_install_oc.py index d5375d6d5..ef4ef4c8f 100644 --- a/opencore_legacy_patcher/wx_gui/gui_install_oc.py +++ b/opencore_legacy_patcher/wx_gui/gui_install_oc.py @@ -103,9 +103,7 @@ class InstallOCFrame(wx.Frame): thread = threading.Thread(target=self._fetch_disks) thread.start() - while thread.is_alive(): - wx.Yield() - continue + gui_support.wait_for_thread(thread) self.progress_bar_animation.stop_pulse() self.progress_bar.Hide() @@ -281,8 +279,7 @@ class InstallOCFrame(wx.Frame): thread = threading.Thread(target=self._install_oc, args=(partition,)) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) if self.result is True: if self.constants.update_stage != gui_support.AutoUpdateStages.INACTIVE and self.constants.detected_os >= os_data.os_data.big_sur: diff --git a/opencore_legacy_patcher/wx_gui/gui_macos_installer_download.py b/opencore_legacy_patcher/wx_gui/gui_macos_installer_download.py index 27da9501f..a05a271e2 100644 --- a/opencore_legacy_patcher/wx_gui/gui_macos_installer_download.py +++ b/opencore_legacy_patcher/wx_gui/gui_macos_installer_download.py @@ -149,8 +149,7 @@ class macOSInstallerDownloadFrame(wx.Frame): thread = threading.Thread(target=_fetch_installers) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) progress_bar_animation.stop_pulse() progress_bar.Hide() @@ -412,8 +411,7 @@ class macOSInstallerDownloadFrame(wx.Frame): self.Show() # Wait for thread to finish - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) progress_bar_animation.stop_pulse() progress_bar.Hide() diff --git a/opencore_legacy_patcher/wx_gui/gui_macos_installer_flash.py b/opencore_legacy_patcher/wx_gui/gui_macos_installer_flash.py index e25923c05..80913b548 100644 --- a/opencore_legacy_patcher/wx_gui/gui_macos_installer_flash.py +++ b/opencore_legacy_patcher/wx_gui/gui_macos_installer_flash.py @@ -88,8 +88,7 @@ class macOSInstallerFlashFrame(wx.Frame): thread = threading.Thread(target=fetch_installers) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) frame_modal = wx.Dialog(self, title=self.title, size=(350, 200)) @@ -180,8 +179,7 @@ class macOSInstallerFlashFrame(wx.Frame): thread = threading.Thread(target=_fetch_disks) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) self.frame_modal = wx.Dialog(self, title=self.title, size=(350, 200)) @@ -317,7 +315,9 @@ class macOSInstallerFlashFrame(wx.Frame): except: bytes_written = 0 wx.CallAfter(progress_bar.SetValue, bytes_written) + wx.Yield() + time.sleep(self.constants.thread_sleep_interval) if self.result is False: logging.error("Failed to flash installer, cannot continue.") @@ -370,8 +370,7 @@ class macOSInstallerFlashFrame(wx.Frame): thread = threading.Thread(target=prepare_script, args=(self, installer_path, disk, self.constants)) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) return self.prepare_result @@ -399,10 +398,11 @@ class macOSInstallerFlashFrame(wx.Frame): return False logging.info("Successfully created macOS installer") - while thread.is_alive(): - # wait for download_thread to finish - # though highly unlikely this thread is still alive (flashing an Installer will take a while) - time.sleep(0.1) + + # wait for download_thread to finish + # though highly unlikely this thread is still alive (flashing an Installer will take a while) + gui_support.wait_for_thread(thread) + logging.info("Installing Root Patcher to drive") self._install_installer_pkg(disk) @@ -617,8 +617,7 @@ class macOSInstallerFlashFrame(wx.Frame): thread = threading.Thread(target=_integrity_check) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) if error_message == "": logging.info("Installer pkg validated") diff --git a/opencore_legacy_patcher/wx_gui/gui_support.py b/opencore_legacy_patcher/wx_gui/gui_support.py index 2cac53fa3..5882833d3 100644 --- a/opencore_legacy_patcher/wx_gui/gui_support.py +++ b/opencore_legacy_patcher/wx_gui/gui_support.py @@ -263,6 +263,19 @@ class ThreadHandler(logging.Handler): wx.CallAfter(self.text_box.AppendText, self.format(record) + '\n') +def wait_for_thread(thread: threading.Thread, sleep_interval=None): + """ + Waits for a thread to finish while processing UI events at regular intervals + to prevent UI freezing and excessive CPU usage. + """ + # Use the passed sleep_interval, or get from global_constants + interval = sleep_interval if sleep_interval is not None else constants.Constants().thread_sleep_interval + + while thread.is_alive(): + wx.Yield() + time.sleep(interval) + + class RestartHost: """ Restarts the host machine diff --git a/opencore_legacy_patcher/wx_gui/gui_sys_patch_display.py b/opencore_legacy_patcher/wx_gui/gui_sys_patch_display.py index 747e5f60b..082f21ec8 100644 --- a/opencore_legacy_patcher/wx_gui/gui_sys_patch_display.py +++ b/opencore_legacy_patcher/wx_gui/gui_sys_patch_display.py @@ -93,9 +93,7 @@ class SysPatchDisplayFrame(wx.Frame): frame.ShowWindowModal() - while thread.is_alive(): - wx.Yield() - + gui_support.wait_for_thread(thread) frame.Close() diff --git a/opencore_legacy_patcher/wx_gui/gui_sys_patch_start.py b/opencore_legacy_patcher/wx_gui/gui_sys_patch_start.py index 75d1695ea..7f0a71c74 100644 --- a/opencore_legacy_patcher/wx_gui/gui_sys_patch_start.py +++ b/opencore_legacy_patcher/wx_gui/gui_sys_patch_start.py @@ -88,8 +88,7 @@ class SysPatchStartFrame(wx.Frame): kdk_thread = threading.Thread(target=_kdk_thread_spawn) kdk_thread.start() - while kdk_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(kdk_thread) if self.kdk_obj.success is False: progress_bar_animation.stop_pulse() @@ -170,8 +169,7 @@ class SysPatchStartFrame(wx.Frame): metallib_thread = threading.Thread(target=_metallib_thread_spawn) metallib_thread.start() - while metallib_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(metallib_thread) if self.metallib_obj.success is False: progress_bar_animation.stop_pulse() @@ -209,8 +207,7 @@ class SysPatchStartFrame(wx.Frame): install_thread = threading.Thread(target=_install_metallib) install_thread.start() - while install_thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(install_thread) if self.result is False: progress_bar_animation.stop_pulse() @@ -314,6 +311,7 @@ class SysPatchStartFrame(wx.Frame): while gui_support.PayloadMount(self.constants, self).is_unpack_finished() is False: wx.Yield() + time.sleep(self.constants.thread_sleep_interval) if self.patches[HardwarePatchsetSettings.KERNEL_DEBUG_KIT_REQUIRED] is True: if self._kdk_download(self) is False: @@ -329,8 +327,7 @@ class SysPatchStartFrame(wx.Frame): thread = threading.Thread(target=self._start_root_patching, args=(self.patches,)) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) self._post_patch() self.return_button.Enable() @@ -356,8 +353,7 @@ class SysPatchStartFrame(wx.Frame): thread = threading.Thread(target=self._revert_root_patching, args=(self.patches,)) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) self._post_patch() self.return_button.Enable() diff --git a/opencore_legacy_patcher/wx_gui/gui_update.py b/opencore_legacy_patcher/wx_gui/gui_update.py index 14a6a3841..d1902f94a 100644 --- a/opencore_legacy_patcher/wx_gui/gui_update.py +++ b/opencore_legacy_patcher/wx_gui/gui_update.py @@ -102,8 +102,7 @@ class UpdateFrame(wx.Frame): thread = threading.Thread(target=_fetch_update) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) gui_download.DownloadFrame( self.frame, @@ -128,8 +127,7 @@ class UpdateFrame(wx.Frame): thread = threading.Thread(target=self._extract_update) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) # Title: Installing update title_label.SetLabel("Installing update...") @@ -138,8 +136,7 @@ class UpdateFrame(wx.Frame): thread = threading.Thread(target=self._install_update) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) # Title: Update complete title_label.SetLabel("Update complete!") @@ -170,8 +167,7 @@ class UpdateFrame(wx.Frame): thread = threading.Thread(target=self._launch_update) thread.start() - while thread.is_alive(): - wx.Yield() + gui_support.wait_for_thread(thread) timer = 5 while True: