Files
I-SecretUpdate/utils/async_worker.py
T
2025-12-19 12:58:58 +01:00

109 lines
2.9 KiB
Python

"""
Async Worker Module
Provides a persistent async worker thread for executing coroutines.
Solves "Event loop is closed" errors by maintaining a single event loop.
"""
import asyncio
import threading
from typing import Any, Optional
from concurrent.futures import Future
import logging
class AsyncWorker:
"""
Persistent async worker thread for executing coroutines.
This worker maintains a single event loop for the application's lifetime,
solving the "Event loop is closed" error by ensuring all async operations
use the same loop and credentials remain valid.
"""
def __init__(self):
self.loop: Optional[asyncio.AbstractEventLoop] = None
self.thread: Optional[threading.Thread] = None
self.running = False
self.logger = logging.getLogger(__name__)
def start(self):
"""Start the async worker thread."""
if self.running:
return
self.running = True
self.thread = threading.Thread(
target=self._run_loop,
daemon=True,
name="AsyncWorker"
)
self.thread.start()
# Wait for loop to be ready
import time
while self.loop is None:
time.sleep(0.01)
def _run_loop(self):
"""Run the event loop in the worker thread."""
self.loop = asyncio.new_event_loop()
asyncio.set_event_loop(self.loop)
self.logger.info("AsyncWorker event loop started")
try:
self.loop.run_forever()
finally:
self.loop.close()
self.logger.info("AsyncWorker event loop closed")
def submit(self, coro) -> Future:
"""
Submit a coroutine to be executed in the worker loop.
Args:
coro: Coroutine to execute
Returns:
Future that will contain the result
"""
if not self.running:
raise RuntimeError("AsyncWorker not started")
result_future = Future()
def callback():
"""Execute coroutine and set result in future."""
try:
task = asyncio.ensure_future(coro, loop=self.loop)
def done_callback(task_future):
try:
result = task_future.result()
result_future.set_result(result)
except Exception as e:
result_future.set_exception(e)
task.add_done_callback(done_callback)
except Exception as e:
result_future.set_exception(e)
self.loop.call_soon_threadsafe(callback)
return result_future
def stop(self):
"""Stop the async worker thread."""
if not self.running:
return
self.running = False
if self.loop:
self.loop.call_soon_threadsafe(self.loop.stop)
if self.thread:
self.thread.join(timeout=5.0)
self.logger.info("AsyncWorker stopped")