Stop the relay server (idempotent, concurrency-safe). Uses ``_stop_lock`` to serialize concurrent calls (e.g. signal handler and ``finally`` block). Each cleanup step is individually guarded so that a partial failure does not prevent subsequent resources from being r
(self)
| 139 | await self._server.serve_forever() |
| 140 | |
| 141 | async def stop(self) -> None: |
| 142 | """Stop the relay server (idempotent, concurrency-safe). |
| 143 | |
| 144 | Uses ``_stop_lock`` to serialize concurrent calls (e.g. signal handler |
| 145 | and ``finally`` block). Each cleanup step is individually guarded so |
| 146 | that a partial failure does not prevent subsequent resources from being |
| 147 | released. |
| 148 | """ |
| 149 | async with self._stop_lock: |
| 150 | if self._stopped: |
| 151 | return |
| 152 | logger.info("Stopping Relay Server...") |
| 153 | self._running = False |
| 154 | |
| 155 | # Cancel all heartbeat tasks |
| 156 | for task in self._heartbeat_tasks.values(): |
| 157 | task.cancel() |
| 158 | self._heartbeat_tasks.clear() |
| 159 | |
| 160 | # Cancel pending commands |
| 161 | for future in self._pending_commands.values(): |
| 162 | if not future.done(): |
| 163 | future.cancel() |
| 164 | self._pending_commands.clear() |
| 165 | |
| 166 | # Close all instances |
| 167 | try: |
| 168 | await self.registry.close_all() |
| 169 | except Exception: |
| 170 | logger.exception("Error closing instances") |
| 171 | |
| 172 | # Stop cache cleanup |
| 173 | try: |
| 174 | await self.request_cache.stop() |
| 175 | except Exception: |
| 176 | logger.exception("Error stopping request cache") |
| 177 | |
| 178 | # Close server |
| 179 | if self._server: |
| 180 | try: |
| 181 | self._server.close() |
| 182 | await self._server.wait_closed() |
| 183 | except Exception: |
| 184 | logger.exception("Error closing server") |
| 185 | |
| 186 | self._stopped = True |
| 187 | logger.info("Relay Server stopped") |
| 188 | |
| 189 | async def _handle_connection( |
| 190 | self, |