Further json isolation and endpoint implementation
Further isolated json specifics to their own module under protocols/json Moved global packet classes to their own protocols/packets module Implemented ConnectorEndpoint and ConnectorRegistry as transport agnostic connector endpoints for universal connector handling, exposed a ConnectorRegistry API that will need to be made available to the JsonServer for it to be able to register and unregister endpoints as it opens and closes connections Implemented a top down shutdown architecture where each object is responsible for its children, this allows for a logical and heirachical flow, each object has its own stop event, which when its stop method is called, it sets and then awaits the stopped event to be set by any worker threads etc, with a mandatory timeout to avoid hanging on shutdown
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
# Copyright (C) 2026 Hector van der Aa <hector@h3cx.dev>
|
||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from queue import Empty, Full, Queue
|
||||
from threading import RLock, Thread
|
||||
import threading
|
||||
from uuid import UUID
|
||||
from dynalab_core.protocols.errors import (
|
||||
ConnectorEndpointQueueFullError,
|
||||
ConnectorRegistryAlreadyRegisteredError,
|
||||
)
|
||||
from dynalab_core.protocols.packets import ProtocolMessage
|
||||
from dynalab_core.protocols.packets.handshake import ConnectorHello
|
||||
|
||||
|
||||
class ConnectorEndpoint:
|
||||
def __init__(self, hello: ConnectorHello) -> None:
|
||||
self._packet_ingress_queue: Queue[ProtocolMessage] = Queue(524288)
|
||||
self._packet_egress_queue: Queue[ProtocolMessage] = Queue(524288)
|
||||
self._connector_hello = hello
|
||||
self._worker_thread = Thread(
|
||||
target=self._worker,
|
||||
name=f"endpoint_worker_{self._connector_hello.connector_uuid}",
|
||||
daemon=True,
|
||||
)
|
||||
self._stop_event = threading.Event()
|
||||
self._stopped_event = threading.Event()
|
||||
|
||||
def stop(self) -> None:
|
||||
self._stop_event.set()
|
||||
if not self._worker_thread.is_alive():
|
||||
self._stopped_event.set()
|
||||
self._stopped_event.wait(10)
|
||||
|
||||
def uuid(self) -> UUID:
|
||||
return self._connector_hello.connector_uuid
|
||||
|
||||
def put_ingress_packet(self, packet: ProtocolMessage) -> None:
|
||||
try:
|
||||
self._packet_ingress_queue.put_nowait(packet)
|
||||
except Full:
|
||||
raise ConnectorEndpointQueueFullError
|
||||
|
||||
def get_egress_packet(self, timeout: float | None) -> ProtocolMessage | None:
|
||||
try:
|
||||
packet = self._packet_egress_queue.get(block=True, timeout=timeout)
|
||||
except Empty:
|
||||
return None
|
||||
else:
|
||||
return packet
|
||||
|
||||
def get_egress_packet_no_wait(self) -> ProtocolMessage | None:
|
||||
try:
|
||||
packet = self._packet_egress_queue.get_nowait()
|
||||
except Empty:
|
||||
return None
|
||||
else:
|
||||
return packet
|
||||
|
||||
def _get_ingress_packet(self, timeout: float | None) -> ProtocolMessage | None:
|
||||
try:
|
||||
packet = self._packet_ingress_queue.get(block=True, timeout=timeout)
|
||||
except Empty:
|
||||
return None
|
||||
else:
|
||||
return packet
|
||||
|
||||
def _get_ingress_packet_no_wait(self) -> ProtocolMessage | None:
|
||||
try:
|
||||
packet = self._packet_ingress_queue.get_nowait()
|
||||
except Empty:
|
||||
return None
|
||||
else:
|
||||
return packet
|
||||
|
||||
def _put_egress_packet(self, packet: ProtocolMessage) -> None:
|
||||
try:
|
||||
self._packet_egress_queue.put_nowait(packet)
|
||||
except Full:
|
||||
raise ConnectorEndpointQueueFullError
|
||||
|
||||
def _worker(self) -> None:
|
||||
# TODO: build endpoint worker thread
|
||||
pass
|
||||
|
||||
|
||||
class ConnectorRegistry:
|
||||
def __init__(self) -> None:
|
||||
self._endpoints: dict[UUID, ConnectorEndpoint] = {}
|
||||
self._lock = RLock()
|
||||
|
||||
def register(self, hello: ConnectorHello) -> None:
|
||||
endpoint = ConnectorEndpoint(hello)
|
||||
|
||||
with self._lock:
|
||||
current = self._endpoints.get(hello.connector_uuid)
|
||||
|
||||
if current is not None:
|
||||
raise ConnectorRegistryAlreadyRegisteredError
|
||||
|
||||
self._endpoints[hello.connector_uuid] = endpoint
|
||||
|
||||
return endpoint
|
||||
|
||||
def unregister(self, endpoint: ConnectorEndpoint) -> None:
|
||||
with self._lock:
|
||||
current = self._endpoints.get(endpoint.uuid())
|
||||
|
||||
if current is endpoint:
|
||||
del self._endpoints[endpoint.uuid()]
|
||||
|
||||
def get(self, connector_uuid: UUID) -> ConnectorEndpoint | None:
|
||||
with self._lock:
|
||||
return self._endpoints.get(connector_uuid)
|
||||
|
||||
def stop(self) -> None:
|
||||
for endpoint in self._endpoints.values():
|
||||
endpoint.stop()
|
||||
Reference in New Issue
Block a user