Fix hang on click and reconnect after daemon restart
Two root causes shared one symptom: the waybar module froze after a click and needed a waybar restart to recover. 1. oc-waybar-ctl used socat's UNIX-SENDTO, a bidirectional relay that can block on a datagram socket. waybar runs the click command on its per-module event thread and stops updating the module until the command returns, so a hung socat froze the module. Replace the socat/nc chain with a bounded python3 sendto (2s timeout). python3 is guaranteed present since the daemon needs it. 2. The daemon ran oc raw json with a blocking subprocess.run on the main loop, so a slow network fetch (up to 60s) stalled the 5s rotation and queued clicks. Move fetch_ocd to a worker thread; the main loop adopts the result when it lands and never blocks. Rotation and click draining stay live during a fetch. Reconnect after a daemon restart now works because the click helper no longer wedges waybar's event thread; the module's interval-1 cat re-reads state.json as soon as the new daemon rewrites it (verified: state.json returns within 1s).
This commit is contained in:
parent
189a0e03ae
commit
c4647dda6b
@ -20,6 +20,7 @@ import signal
|
||||
import socket
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from datetime import datetime, timezone
|
||||
from html import escape
|
||||
@ -303,6 +304,11 @@ class Daemon:
|
||||
self.last_key = None
|
||||
self.sock = None
|
||||
self.running = True
|
||||
# The ocd poll runs in a worker thread so the main loop never
|
||||
# blocks on a slow network fetch. Rotation and clicks stay live.
|
||||
self._fetch_thread = None
|
||||
self._fresh_report = None
|
||||
self._fetch_lock = threading.Lock()
|
||||
|
||||
def event_deadline(self):
|
||||
now = time.monotonic()
|
||||
@ -327,14 +333,27 @@ class Daemon:
|
||||
def lock(self):
|
||||
self.locked_until = time.monotonic() + LOCK_SECS
|
||||
|
||||
def _fetch_worker(self):
|
||||
rep = fetch_ocd()
|
||||
if rep:
|
||||
with self._fetch_lock:
|
||||
self._fresh_report = rep
|
||||
|
||||
def maybe_tick(self):
|
||||
now = time.monotonic()
|
||||
if now >= self.next_poll:
|
||||
# Kick off a poll on a worker thread; never block the main loop.
|
||||
if now >= self.next_poll and (self._fetch_thread is None or not self._fetch_thread.is_alive()):
|
||||
self.next_poll = now + POLL_SECS
|
||||
rep = fetch_ocd()
|
||||
self._fetch_thread = threading.Thread(target=self._fetch_worker, daemon=True)
|
||||
self._fetch_thread.start()
|
||||
# Adopt a finished poll's result if one is ready.
|
||||
if self._fetch_thread is not None and not self._fetch_thread.is_alive():
|
||||
self._fetch_thread = None
|
||||
with self._fetch_lock:
|
||||
rep = self._fresh_report
|
||||
self._fresh_report = None
|
||||
if rep:
|
||||
self.report = rep
|
||||
# keep index in range
|
||||
n = len(rep.get("accounts") or [])
|
||||
if n and self.index >= n:
|
||||
self.index %= n
|
||||
|
||||
@ -1,30 +1,33 @@
|
||||
#!/bin/sh
|
||||
# ocd-waybar-ctl: send a command to the ocd-waybar daemon.
|
||||
# Usage: ocd-waybar-ctl {advance|lock|poll|quit}
|
||||
# wired into the waybar custom/ocd module as on-click / on-click-right.
|
||||
# Wired into the waybar custom/ocd module as on-click / on-click-right.
|
||||
#
|
||||
# This must never hang: waybar runs it on a click and stops updating the
|
||||
# module until it returns. socat's UNIX-SENDTO relay can block waiting on
|
||||
# the datagram socket, so we use a small python sendto with a hard timeout
|
||||
# instead. python3 is guaranteed present because the daemon itself needs it.
|
||||
|
||||
set -eu
|
||||
cmd="${1:-advance}"
|
||||
sock="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ocd-waybar.sock"
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
cmd="$1"
|
||||
fi
|
||||
|
||||
case "$cmd" in
|
||||
advance|lock|poll|quit) ;;
|
||||
*) echo "ocd-waybar-ctl: unknown command '$cmd'" >&2; exit 2 ;;
|
||||
esac
|
||||
|
||||
if [ ! -S "$sock" ]; then
|
||||
echo "ocd-waybar-ctl: daemon socket not found at $sock" >&2
|
||||
exit 1
|
||||
fi
|
||||
sock="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ocd-waybar.sock"
|
||||
[ -S "$sock" ] || { echo "ocd-waybar-ctl: daemon socket not found at $sock" >&2; exit 1; }
|
||||
|
||||
printf '%s\n' "$cmd" | socat - UNIX-SENDTO:"$sock" 2>/dev/null \
|
||||
|| printf '%s\n' "$cmd" | nc -U -u -w1 "$sock" 2>/dev/null \
|
||||
|| python3 - "$sock" "$cmd" <<'PY'
|
||||
python3 - "$sock" "$cmd" <<'PY'
|
||||
import socket, sys
|
||||
sock, cmd = sys.argv[1], sys.argv[2]
|
||||
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
|
||||
s.sendto((sys.argv[2] + "\n").encode(), sys.argv[1])
|
||||
s.settimeout(2.0)
|
||||
try:
|
||||
s.sendto((cmd + "\n").encode(), sock)
|
||||
except (OSError, socket.timeout) as e:
|
||||
print("ocd-waybar-ctl: send failed: %s" % e, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
finally:
|
||||
s.close()
|
||||
PY
|
||||
Loading…
Reference in New Issue
Block a user