raylib networking: remove locking on UI thread (#36063)

* use callback queue to make this thread safe and remove locks (which lag ui thread?)

* woah this works

* no more lock!

* always run signal handler and store callbacks, like qt

* debug

* more

* okay not for now

* combine _get_connections and _connection_by_ssid, closer to qt and not an explosion of GetSettings dbus calls

* debug

* try this

* skip

* len

* skip hidden networks

* actually slower

* stash

* back to 8929f37d49

* clean up
pull/36065/head
Shane Smiskol 1 day ago committed by GitHub
parent 5359f6d354
commit 23b4aaf2a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 26
      system/ui/lib/wifi_manager.py
  2. 12
      system/ui/widgets/network.py

@ -133,12 +133,12 @@ class WifiManager:
# State # State
self._connecting_to_ssid: str = "" self._connecting_to_ssid: str = ""
self._last_network_update: float = 0.0 self._last_network_update: float = 0.0
self._callback_queue: list[Callable] = []
# Callbacks # Callbacks
# TODO: implement a callback queue to avoid blocking UI thread
self._need_auth: Callable[[str], None] | None = None self._need_auth: Callable[[str], None] | None = None
self._activated: Callable[[], None] | None = None self._activated: Callable[[], None] | None = None
self._forgotten: Callable[[str], None] | None = None self._forgotten: Callable[[], None] | None = None
self._networks_updated: Callable[[list[Network]], None] | None = None self._networks_updated: Callable[[list[Network]], None] | None = None
self._disconnected: Callable[[], None] | None = None self._disconnected: Callable[[], None] | None = None
@ -154,7 +154,7 @@ class WifiManager:
def set_callbacks(self, need_auth: Callable[[str], None], def set_callbacks(self, need_auth: Callable[[str], None],
activated: Callable[[], None] | None, activated: Callable[[], None] | None,
forgotten: Callable[[str], None], forgotten: Callable[[], None],
networks_updated: Callable[[list[Network]], None], networks_updated: Callable[[list[Network]], None],
disconnected: Callable[[], None]): disconnected: Callable[[], None]):
self._need_auth = need_auth self._need_auth = need_auth
@ -163,6 +163,15 @@ class WifiManager:
self._networks_updated = networks_updated self._networks_updated = networks_updated
self._disconnected = disconnected self._disconnected = disconnected
def _enqueue_callback(self, cb: Callable, *args):
self._callback_queue.append(lambda: cb(*args))
def process_callbacks(self):
# Call from UI thread to run any pending callbacks
to_run, self._callback_queue = self._callback_queue, []
for cb in to_run:
cb()
def set_active(self, active: bool): def set_active(self, active: bool):
self._active = active self._active = active
@ -187,7 +196,6 @@ class WifiManager:
with self._conn_monitor.filter(rule, bufsize=SIGNAL_QUEUE_SIZE) as q: with self._conn_monitor.filter(rule, bufsize=SIGNAL_QUEUE_SIZE) as q:
while not self._exit: while not self._exit:
# TODO: always run, and ensure callbacks don't block UI thread
if not self._active: if not self._active:
time.sleep(1) time.sleep(1)
continue continue
@ -204,19 +212,19 @@ class WifiManager:
if new_state == NMDeviceState.NEED_AUTH and change_reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and len(self._connecting_to_ssid): if new_state == NMDeviceState.NEED_AUTH and change_reason == NM_DEVICE_STATE_REASON_SUPPLICANT_DISCONNECT and len(self._connecting_to_ssid):
self.forget_connection(self._connecting_to_ssid, block=True) self.forget_connection(self._connecting_to_ssid, block=True)
if self._need_auth is not None: if self._need_auth is not None:
self._need_auth(self._connecting_to_ssid) self._enqueue_callback(self._need_auth, self._connecting_to_ssid)
self._connecting_to_ssid = "" self._connecting_to_ssid = ""
elif new_state == NMDeviceState.ACTIVATED: elif new_state == NMDeviceState.ACTIVATED:
if self._activated is not None: if self._activated is not None:
self._update_networks() self._update_networks()
self._activated() self._enqueue_callback(self._activated)
self._connecting_to_ssid = "" self._connecting_to_ssid = ""
elif new_state == NMDeviceState.DISCONNECTED and change_reason != NM_DEVICE_STATE_REASON_NEW_ACTIVATION: elif new_state == NMDeviceState.DISCONNECTED and change_reason != NM_DEVICE_STATE_REASON_NEW_ACTIVATION:
self._connecting_to_ssid = "" self._connecting_to_ssid = ""
if self._disconnected is not None: if self._disconnected is not None:
self._disconnected() self._enqueue_callback(self._disconnected)
def _network_scanner(self): def _network_scanner(self):
self._wait_for_wifi_device() self._wait_for_wifi_device()
@ -324,7 +332,7 @@ class WifiManager:
if self._forgotten is not None: if self._forgotten is not None:
self._update_networks() self._update_networks()
self._forgotten(ssid) self._enqueue_callback(self._forgotten)
if block: if block:
worker() worker()
@ -400,7 +408,7 @@ class WifiManager:
self._networks = networks self._networks = networks
if self._networks_updated is not None: if self._networks_updated is not None:
self._networks_updated(self._networks) self._enqueue_callback(self._networks_updated, self._networks)
def __del__(self): def __del__(self):
self.stop() self.stop()

@ -1,6 +1,5 @@
from enum import IntEnum from enum import IntEnum
from functools import partial from functools import partial
from threading import Lock
from typing import cast from typing import cast
import pyray as rl import pyray as rl
@ -50,7 +49,6 @@ class WifiManagerUI(Widget):
self._networks: list[Network] = [] self._networks: list[Network] = []
self._networks_buttons: dict[str, Button] = {} self._networks_buttons: dict[str, Button] = {}
self._forget_networks_buttons: dict[str, Button] = {} self._forget_networks_buttons: dict[str, Button] = {}
self._lock = Lock()
self._confirm_dialog = ConfirmDialog("", "Forget", "Cancel") self._confirm_dialog = ConfirmDialog("", "Forget", "Cancel")
self.wifi_manager.set_callbacks(need_auth=self._on_need_auth, self.wifi_manager.set_callbacks(need_auth=self._on_need_auth,
@ -71,7 +69,8 @@ class WifiManagerUI(Widget):
gui_app.texture(icon, ICON_SIZE, ICON_SIZE) gui_app.texture(icon, ICON_SIZE, ICON_SIZE)
def _render(self, rect: rl.Rectangle): def _render(self, rect: rl.Rectangle):
with self._lock: self.wifi_manager.process_callbacks()
if not self._networks: if not self._networks:
gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER) gui_label(rect, "Scanning Wi-Fi networks...", 72, alignment=rl.GuiTextAlignment.TEXT_ALIGN_CENTER)
return return
@ -211,7 +210,6 @@ class WifiManagerUI(Widget):
self.wifi_manager.forget_connection(network.ssid) self.wifi_manager.forget_connection(network.ssid)
def _on_network_updated(self, networks: list[Network]): def _on_network_updated(self, networks: list[Network]):
with self._lock:
self._networks = networks self._networks = networks
for n in self._networks: for n in self._networks:
self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT, self._networks_buttons[n.ssid] = Button(n.ssid, partial(self._networks_buttons_callback, n), font_size=55, text_alignment=TextAlignment.LEFT,
@ -220,7 +218,6 @@ class WifiManagerUI(Widget):
font_size=45) font_size=45)
def _on_need_auth(self, ssid): def _on_need_auth(self, ssid):
with self._lock:
network = next((n for n in self._networks if n.ssid == ssid), None) network = next((n for n in self._networks if n.ssid == ssid), None)
if network: if network:
self.state = UIState.NEEDS_AUTH self.state = UIState.NEEDS_AUTH
@ -228,17 +225,14 @@ class WifiManagerUI(Widget):
self._password_retry = True self._password_retry = True
def _on_activated(self): def _on_activated(self):
with self._lock:
if self.state == UIState.CONNECTING: if self.state == UIState.CONNECTING:
self.state = UIState.IDLE self.state = UIState.IDLE
def _on_forgotten(self, ssid): def _on_forgotten(self):
with self._lock:
if self.state == UIState.FORGETTING: if self.state == UIState.FORGETTING:
self.state = UIState.IDLE self.state = UIState.IDLE
def _on_disconnected(self): def _on_disconnected(self):
with self._lock:
if self.state == UIState.CONNECTING: if self.state == UIState.CONNECTING:
self.state = UIState.IDLE self.state = UIState.IDLE

Loading…
Cancel
Save