diff --git a/addons/linuxthread/bin/liblinuxthread.linux.template_debug.x86_64.so b/addons/linuxthread/bin/liblinuxthread.linux.template_debug.x86_64.so new file mode 100755 index 00000000..fa4ee8e6 Binary files /dev/null and b/addons/linuxthread/bin/liblinuxthread.linux.template_debug.x86_64.so differ diff --git a/addons/linuxthread/bin/liblinuxthread.linux.template_release.x86_64.so b/addons/linuxthread/bin/liblinuxthread.linux.template_release.x86_64.so new file mode 100755 index 00000000..ba579ef3 Binary files /dev/null and b/addons/linuxthread/bin/liblinuxthread.linux.template_release.x86_64.so differ diff --git a/addons/linuxthread/linuxthread.gdextension b/addons/linuxthread/linuxthread.gdextension new file mode 100644 index 00000000..ac6f371b --- /dev/null +++ b/addons/linuxthread/linuxthread.gdextension @@ -0,0 +1,9 @@ +[configuration] + +entry_symbol = "linux_thread_library_init" +compatibility_minimum = 4.1 + +[libraries] + +linux.debug.x86_64 = "res://addons/linuxthread/bin/liblinuxthread.linux.template_debug.x86_64.so" +linux.release.x86_64 = "res://addons/linuxthread/bin/liblinuxthread.linux.template_release.x86_64.so" diff --git a/core/systems/threading/input_thread.tres b/core/systems/threading/input_thread.tres index 7a4a6eea..57d6ad78 100644 --- a/core/systems/threading/input_thread.tres +++ b/core/systems/threading/input_thread.tres @@ -6,3 +6,4 @@ script = ExtResource("1_rbhla") name = "InputThread" target_tick_rate = 600 +niceness = -20 diff --git a/core/systems/threading/io_thread.tres b/core/systems/threading/io_thread.tres index 49904b7f..e8763e56 100644 --- a/core/systems/threading/io_thread.tres +++ b/core/systems/threading/io_thread.tres @@ -6,3 +6,4 @@ script = ExtResource("1_qvlys") name = "IOThread" target_tick_rate = 30 +niceness = 0 diff --git a/core/systems/threading/shared_thread.gd b/core/systems/threading/shared_thread.gd index e53b8c5c..90ff2283 100644 --- a/core/systems/threading/shared_thread.gd +++ b/core/systems/threading/shared_thread.gd @@ -12,6 +12,7 @@ const watchdog := preload("res://core/systems/threading/watchdog_thread.tres") var thread: Thread var mutex := Mutex.new() +var tid := -1 var running := false var nodes: Array[NodeThread] = [] var process_funcs: Array[Callable] = [] @@ -24,6 +25,8 @@ var logger := Log.get_logger("SharedThread", Log.LEVEL.INFO) @export var name := "SharedThread" ## Target rate to run at in ticks per second @export var target_tick_rate := 60 +## Priority (niceness) of the thread +@export var niceness := 0 func _init() -> void: @@ -42,7 +45,7 @@ func start() -> void: running = true thread = Thread.new() thread.start(_run) - logger.info("Thread group started: " + name) + logger.info("Shared thread started: " + name) ## Stops the thread for the thread group @@ -53,7 +56,28 @@ func stop() -> void: running = false mutex.unlock() thread.wait_to_finish() - logger.info("Thread group stopped: " + name) + logger.info("Shared thread stopped: " + name) + + +## Set the given thread niceness to the given value. +## Note: in order to set negative nice value, this must be run: +## setcap 'cap_sys_nice=eip' +func set_priority(value: int) -> int: + # If this was called from another thread, schedule it to run on the thread + mutex.lock() + var thread_id := tid + mutex.unlock() + if LinuxThread.get_tid() != thread_id: + logger.debug("Set thread priority was called from another thread") + return await exec(set_priority.bind(value)) + + # Set the thread priority if this function was called from the SharedThread + var err := LinuxThread.set_thread_priority(value) + if err == OK: + niceness = value + logger.info("Set thread niceness on {0} ({1}) to: {2}".format([name, thread_id, value])) + + return err ## Add the given [NodeThread] to the list of nodes to process. This should @@ -145,6 +169,17 @@ func remove_process(method: Callable) -> void: func _run() -> void: + # Update the thread ID + mutex.lock() + tid = LinuxThread.get_tid() + mutex.unlock() + logger.info("Started thread with thread ID: " + str(LinuxThread.get_tid())) + + # If the nice value isn't default, reassign the thread priority + if niceness != 0: + if await set_priority(niceness) != OK: + logger.warn("Unable to set niceness on thread: " + name) + # TODO: Fix unsafe thread operations Thread.set_thread_safety_checks_enabled(false) var exited := false diff --git a/core/systems/threading/system_thread.tres b/core/systems/threading/system_thread.tres index 1d32e507..86194827 100644 --- a/core/systems/threading/system_thread.tres +++ b/core/systems/threading/system_thread.tres @@ -6,3 +6,4 @@ script = ExtResource("1_hqprx") name = "SystemThread" target_tick_rate = 600 +niceness = 0 diff --git a/core/systems/threading/utility_thread.tres b/core/systems/threading/utility_thread.tres index eeacbb81..990af678 100644 --- a/core/systems/threading/utility_thread.tres +++ b/core/systems/threading/utility_thread.tres @@ -6,3 +6,4 @@ script = ExtResource("1_svd22") name = "UtilityThread" target_tick_rate = 10 +niceness = 0 diff --git a/core/systems/threading/watchdog_thread.tres b/core/systems/threading/watchdog_thread.tres index 9b4440c2..7578b4ce 100644 --- a/core/systems/threading/watchdog_thread.tres +++ b/core/systems/threading/watchdog_thread.tres @@ -6,3 +6,4 @@ script = ExtResource("1_fg543") name = "WatchdogThread" target_tick_rate = 1 +warn_after_num_missed_frames = 20 diff --git a/entrypoint.gd b/entrypoint.gd index 1872736c..3a57f3f3 100644 --- a/entrypoint.gd +++ b/entrypoint.gd @@ -129,6 +129,10 @@ func _apply_update_packs() -> void: if OS.execute("chmod", ["+x", file_path]) != OK: logger.warn("Failed to set execute permissions. Not loading update pack") return + + # Try to run setcap to allow setting thread priorities + if OS.execute("/usr/share/opengamepadui/scripts/make_nice", []) != OK: + logger.warn("Unable to run setcap to enable thread priorities") # Launch the update pack executable and exit var update_bin := ProjectSettings.globalize_path(update_pack_entrypoint) diff --git a/rootfs/Makefile b/rootfs/Makefile index 71e2122c..397791df 100644 --- a/rootfs/Makefile +++ b/rootfs/Makefile @@ -21,21 +21,34 @@ install: ## Install OpenGamepadUI (default: ~/.local) install -Dm644 usr/share/icons/hicolor/scalable/apps/opengamepadui.svg \ $(PREFIX)/share/icons/hicolor/scalable/apps/opengamepadui.svg mkdir -p $(PREFIX)/share/opengamepadui + install -Dm644 usr/share/opengamepadui/libdbus.linux.template_debug.x86_64.so \ + $(PREFIX)/share/opengamepadui/libdbus.linux.template_debug.x86_64.so install -Dm644 usr/share/opengamepadui/libevdev.linux.template_debug.x86_64.so \ $(PREFIX)/share/opengamepadui/libevdev.linux.template_debug.x86_64.so - install -Dm644 usr/share/opengamepadui/libxlib.linux.template_debug.x86_64.so \ - $(PREFIX)/share/opengamepadui/libxlib.linux.template_debug.x86_64.so + install -Dm644 usr/share/opengamepadui/liblinuxthread.linux.template_debug.x86_64.so \ + $(PREFIX)/share/opengamepadui/liblinuxthread.linux.template_debug.x86_64.so install -Dm644 usr/share/opengamepadui/libopensd.linux.template_debug.x86_64.so \ $(PREFIX)/share/opengamepadui/libopensd.linux.template_debug.x86_64.so install -Dm644 usr/share/opengamepadui/libpty.linux.template_debug.x86_64.so \ $(PREFIX)/share/opengamepadui/libpty.linux.template_debug.x86_64.so + install -Dm644 usr/share/opengamepadui/libxlib.linux.template_debug.x86_64.so \ + $(PREFIX)/share/opengamepadui/libxlib.linux.template_debug.x86_64.so install -Dm755 usr/share/opengamepadui/opengamepad-ui.x86_64 \ $(PREFIX)/share/opengamepadui/opengamepad-ui.x86_64 + setcap 'cap_sys_nice=eip' $(PREFIX)/share/opengamepadui/opengamepad-ui.x86_64 || true mkdir -p $(PREFIX)/share/opengamepadui/scripts install -Dm755 usr/share/opengamepadui/scripts/powertools \ $(PREFIX)/share/opengamepadui/scripts/powertools + install -Dm755 usr/share/opengamepadui/scripts/manage_input \ + $(PREFIX)/share/opengamepadui/scripts/manage_input + install -Dm755 usr/share/opengamepadui/scripts/make_nice \ + $(PREFIX)/share/opengamepadui/scripts/make_nice install -Dm644 usr/share/polkit-1/actions/org.shadowblip.powertools.policy \ $(PREFIX)/share/polkit-1/actions/org.shadowblip.powertools.policy + install -Dm644 usr/share/polkit-1/actions/org.shadowblip.manage_input.policy \ + $(PREFIX)/share/polkit-1/actions/org.shadowblip.manage_input.policy + install -Dm644 usr/share/polkit-1/actions/org.shadowblip.setcap.policy \ + $(PREFIX)/share/polkit-1/actions/org.shadowblip.setcap.policy install -Dm644 usr/lib/udev/hwdb.d/59-opengamepadui-handheld.hwdb \ $(PREFIX)/lib/udev/hwdb.d/59-opengamepadui-handheld.hwdb install -Dm644 usr/lib/udev/rules.d/61-opengamepadui-handheld.rules \ diff --git a/rootfs/usr/share/opengamepadui/scripts/make_nice b/rootfs/usr/share/opengamepadui/scripts/make_nice new file mode 100755 index 00000000..7894d65a --- /dev/null +++ b/rootfs/usr/share/opengamepadui/scripts/make_nice @@ -0,0 +1,10 @@ +#!/bin/bash +set -eu + +if [[ $EUID -ne 0 ]]; then + exec pkexec --disable-internal-agent "$0" "$HOME" "$@" +fi + +USER_HOME="$1" +UPDATE_PATH="${USER_HOME}/.local/share/opengamepadui/updates/opengamepad-ui.x86_64" +setcap 'cap_sys_nice=eip' "${UPDATE_PATH}" diff --git a/rootfs/usr/share/polkit-1/actions/org.shadowblip.setcap.policy b/rootfs/usr/share/polkit-1/actions/org.shadowblip.setcap.policy new file mode 100644 index 00000000..0720fbdb --- /dev/null +++ b/rootfs/usr/share/polkit-1/actions/org.shadowblip.setcap.policy @@ -0,0 +1,16 @@ + + + + ShadowBlip + http://www.github.com/shadowblip + + Allow negative nice values + package-x-generic + + yes + yes + yes + + /usr/share/opengamepadui/scripts/make_nice + +