| 725 | self._main_task = self._loop.create_task(run_server()) |
| 726 | |
| 727 | def run_loop(): |
| 728 | asyncio.set_event_loop(self._loop) |
| 729 | try: |
| 730 | self._loop.run_until_complete(self._main_task) |
| 731 | except RuntimeError as e: |
| 732 | # This can happen if the event loop is stopped while futures are pending |
| 733 | error_str = str(e) |
| 734 | if "Event loop stopped before Future completed" in error_str: |
| 735 | # This is expected during shutdown - ignore it |
| 736 | logger.debug( |
| 737 | f"[server] Expected shutdown error: {error_str}") |
| 738 | else: |
| 739 | # This is an unexpected RuntimeError - log full details |
| 740 | import traceback |
| 741 | logger.error(f"Event loop error: {error_str}") |
| 742 | logger.error(f"Traceback: {traceback.format_exc()}") |
| 743 | except Exception as e: |
| 744 | logger.error(f"Event loop error: {e}") |
| 745 | finally: |
| 746 | # Clean up any remaining tasks |
| 747 | pending = asyncio.all_tasks(self._loop) |
| 748 | for task in pending: |
| 749 | task.cancel() |
| 750 | if pending: |
| 751 | try: |
| 752 | self._loop.run_until_complete( |
| 753 | asyncio.gather(*pending, return_exceptions=True)) |
| 754 | except RuntimeError: |
| 755 | # Event loop might already be closed |
| 756 | pass |
| 757 | self._loop.close() |
| 758 | |
| 759 | self._server_thread = threading.Thread(target=run_loop, |
| 760 | name="rpc_server_thread", |