clan-app: Moved thread handling up to the ApiBridge
This commit is contained in:
@@ -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]
|
||||
)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user