From 5567d67305751efd3b41107f7518cfbebe6f0f56 Mon Sep 17 00:00:00 2001 From: Omar Amin <65174210+omaramin-2000@users.noreply.github.com> Date: Tue, 27 Jan 2026 21:54:20 +0200 Subject: [PATCH] Check for empty messages and handle thread-safety --- linux_voice_assistant/api_server.py | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/linux_voice_assistant/api_server.py b/linux_voice_assistant/api_server.py index c0685c8..574ae38 100644 --- a/linux_voice_assistant/api_server.py +++ b/linux_voice_assistant/api_server.py @@ -2,6 +2,7 @@ import asyncio import logging +import threading from abc import abstractmethod from collections.abc import Iterable from typing import TYPE_CHECKING, List, Optional @@ -36,7 +37,9 @@ class APIServer(asyncio.Protocol): self._pos: int = 0 self._transport = None self._writelines = None - + self._loop: Optional[asyncio.AbstractEventLoop] = None + self._loop_thread_id: Optional[int] = None + @abstractmethod def handle_message(self, msg: message.Message) -> Iterable[message.Message]: pass @@ -75,7 +78,7 @@ class APIServer(asyncio.Protocol): self.send_messages(msgs) def send_messages(self, msgs: List[message.Message]): - if self._writelines is None: + if self._writelines is None or not msgs: return packets = [ @@ -83,12 +86,26 @@ class APIServer(asyncio.Protocol): for msg in msgs ] packet_bytes = make_plain_text_packets(packets) + if ( + self._loop is not None + and self._loop_thread_id is not None + and threading.get_ident() != self._loop_thread_id + ): + self._loop.call_soon_threadsafe(self._writelines, packet_bytes) + return + self._writelines(packet_bytes) def connection_made(self, transport) -> None: self._transport = transport self._writelines = transport.writelines - + try: + self._loop = asyncio.get_running_loop() + except RuntimeError: + self._loop = None + else: + self._loop_thread_id = threading.get_ident() + def data_received(self, data: bytes): if self._buffer is None: self._buffer = data @@ -141,7 +158,9 @@ class APIServer(asyncio.Protocol): def connection_lost(self, exc): self._transport = None self._writelines = None - + self._loop = None + self._loop_thread_id = None + def _read_varuint(self) -> int: """Read a varuint from the buffer or -1 if the buffer runs out of bytes.""" if not self._buffer: