mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-10-24 07:24:42 +03:00
wake the mqtt worker when a publish is queued (#24580)
publish() only queued the message, and the worker was blocked waiting on the broker socket for up to a second, so on a quiet connection messages went out up to 1s late. A socketpair registered in the worker's selector now interrupts the wait when a publish is queued or stop() is called.
This commit is contained in:
+29
-6
@@ -3,6 +3,7 @@ from __future__ import annotations
|
||||
import logging
|
||||
import queue
|
||||
import selectors
|
||||
import socket
|
||||
import threading
|
||||
import time
|
||||
from collections.abc import Callable
|
||||
@@ -57,6 +58,11 @@ class MqttClient(Communicator):
|
||||
self._next_connect_time = 0.0
|
||||
self._last_on_connect_dispatch = 0.0
|
||||
|
||||
# lets other threads interrupt the worker's socket wait
|
||||
self._wake_recv, self._wake_send = socket.socketpair()
|
||||
self._wake_recv.setblocking(False)
|
||||
self._wake_send.setblocking(False)
|
||||
|
||||
def subscribe(self, receiver: Callable) -> None:
|
||||
"""Wrapper for allowing dispatcher to subscribe."""
|
||||
self._dispatcher = receiver
|
||||
@@ -86,6 +92,7 @@ class MqttClient(Communicator):
|
||||
return
|
||||
|
||||
self._publish_queue.put(QueuedPublish(full_topic, payload, retain))
|
||||
self._wake_worker()
|
||||
|
||||
def stop(self) -> None:
|
||||
if self._worker is None:
|
||||
@@ -101,9 +108,11 @@ class MqttClient(Communicator):
|
||||
publish_done,
|
||||
)
|
||||
)
|
||||
self._wake_worker()
|
||||
publish_done.wait(MQTT_SHUTDOWN_FLUSH_TIMEOUT)
|
||||
|
||||
self._stop_event.set()
|
||||
self._wake_worker()
|
||||
|
||||
if self.client is not None:
|
||||
try:
|
||||
@@ -369,11 +378,17 @@ class MqttClient(Communicator):
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
def _wake_worker(self) -> None:
|
||||
try:
|
||||
self._wake_send.send(b"\0")
|
||||
except BlockingIOError:
|
||||
# the buffer is full, so a wake is already pending
|
||||
pass
|
||||
|
||||
def _loop_client(self, timeout: float) -> int:
|
||||
"""Drive Paho without select()'s limit on socket file descriptors."""
|
||||
assert self.client is not None
|
||||
client = self.client
|
||||
if client is None:
|
||||
return mqtt.MQTT_ERR_NO_CONN
|
||||
sock = client.socket()
|
||||
if sock is None:
|
||||
return mqtt.MQTT_ERR_NO_CONN
|
||||
@@ -385,11 +400,19 @@ class MqttClient(Communicator):
|
||||
pending = hasattr(sock, "pending") and sock.pending() > 0
|
||||
with selectors.DefaultSelector() as selector:
|
||||
selector.register(sock, events)
|
||||
ready = selector.select(0.0 if pending else timeout)
|
||||
selector.register(self._wake_recv, selectors.EVENT_READ)
|
||||
ready = {
|
||||
key.fileobj: mask
|
||||
for key, mask in selector.select(0.0 if pending else timeout)
|
||||
}
|
||||
|
||||
ready_events = 0
|
||||
for _, mask in ready:
|
||||
ready_events |= mask
|
||||
if self._wake_recv in ready:
|
||||
try:
|
||||
self._wake_recv.recv(4096)
|
||||
except BlockingIOError:
|
||||
pass
|
||||
|
||||
ready_events = ready.get(sock, 0)
|
||||
if pending or ready_events & selectors.EVENT_READ:
|
||||
result = client.loop_read()
|
||||
if result != mqtt.MQTT_ERR_SUCCESS or client.socket() is None:
|
||||
|
||||
Reference in New Issue
Block a user