clan-app: Moved thread handling up to the ApiBridge

This commit is contained in:
Qubasa
2025-07-10 12:02:30 +07:00
parent dbe40e6f5b
commit 70f7f7e676
8 changed files with 94 additions and 61 deletions

View File

@@ -1,10 +1,13 @@
import logging
import threading
from abc import ABC, abstractmethod
from contextlib import ExitStack
from dataclasses import dataclass
from dataclasses import dataclass, field
from typing import TYPE_CHECKING, Any
from clan_lib.api import ApiResponse
from clan_lib.api.tasks import WebThread
from clan_lib.async_run import set_should_cancel
if TYPE_CHECKING:
from .middleware import Middleware
@@ -32,6 +35,7 @@ class ApiBridge(ABC):
"""Generic interface for API bridges that can handle method calls from different sources."""
middleware_chain: tuple["Middleware", ...]
threads: dict[str, WebThread] = field(default_factory=dict)
@abstractmethod
def send_api_response(self, response: BackendResponse) -> None:
@@ -87,3 +91,50 @@ class ApiBridge(ABC):
)
self.send_api_response(response)
def process_request_in_thread(
self,
request: BackendRequest,
*,
thread_name: str = "ApiBridgeThread",
wait_for_completion: bool = False,
timeout: float = 60.0,
) -> None:
"""Process an API request in a separate thread with cancellation support.
Args:
request: The API request to process
thread_name: Name for the thread (for debugging)
wait_for_completion: Whether to wait for the thread to complete
timeout: Timeout in seconds when waiting for completion
"""
op_key = request.op_key or "unknown"
def thread_task(stop_event: threading.Event) -> None:
set_should_cancel(lambda: stop_event.is_set())
try:
log.debug(
f"Processing {request.method_name} with args {request.args} "
f"and header {request.header} in thread {thread_name}"
)
self.process_request(request)
finally:
self.threads.pop(op_key, None)
stop_event = threading.Event()
thread = threading.Thread(
target=thread_task, args=(stop_event,), name=thread_name
)
thread.start()
self.threads[op_key] = WebThread(thread=thread, stop_event=stop_event)
if wait_for_completion:
# Wait for the thread to complete (this blocks until response is sent)
thread.join(timeout=timeout)
# Handle timeout
if thread.is_alive():
stop_event.set() # Cancel the thread
self.send_api_error_response(
op_key, "Request timeout", ["api_bridge", request.method_name]
)

View File

@@ -28,7 +28,7 @@ def open_file(
GLib.idle_add(gtk_open_file, file_request, op_key)
while RESULT.get(op_key) is None:
time.sleep(0.2)
time.sleep(0.1)
response = RESULT[op_key]
del RESULT[op_key]
return response