Repository: redis/redis-py
File: redis/auth/token_manager.py (Python)

Code before:

            # Use threading.Event to signal when loop is ready
            loop_ready = threading.Event()

            def start_loop():
                asyncio.set_event_loop(loop)
                loop_ready.set()  # Signal that loop is ready
                loop.run_forever()

            thread = threading.Thread(target=start_loop, daemon=True)
            thread.start()

            # Wait for the loop to be ready before scheduling
            loop_ready.wait()

        # Use thread-safe Event for cross-thread synchronization
        init_done = threading.Event()

        def renew_with_callback():
            try:
                self._renew_token(skip_initial)
            finally:
                init_done.set()

        # Schedule using call_soon_threadsafe for thread-safe scheduling
        loop.call_soon_threadsafe(renew_with_callback)
        logger.info("Token manager started")

        # Blocks using thread-safe Event
        init_done.wait()
        return self.stop

    async def start_async(
        self,
        listener: CredentialsListener,
        block_for_initial: bool = False,
        initial_delay_in_ms: float = 0,
        skip_initial: bool = False,
    ) -> Callable[[], None]:
        self._listener = listener

        loop = asyncio.get_running_loop()
        init_event = asyncio.Event()

        # Wraps the async callback with async wrapper to schedule with loop.call_later()
        wrapped = _async_to_sync_wrapper(
            loop, self._renew_token_async, skip_initial, init_event
        )
        self._init_timer = loop.call_later(initial_delay_in_ms / 1000, wrapped)
        logger.info("Token manager started")


Review comment (suggestion):
The return value from `loop.call_soon_threadsafe(renew_with_callback)` is not being assigned to `self._init_timer`. This means that if `stop()` is called before the initial token renewal completes, the `stop()` method (which checks and cancels `self._init_timer` at lines 205-206) won't be able to cancel the scheduled callback. This could lead to the callback executing even after `stop()` has been called. Assign the handle to `self._init_timer` to enable proper cleanup.
```suggestion
        self._init_timer = loop.call_soon_threadsafe(renew_with_callback)
```

Code after:

            # Use threading.Event to signal when loop is ready
            loop_ready = threading.Event()

            def start_loop():
                # This runs in the background thread. First, bind the event loop to
                # this thread, then signal that the loop is ready so the calling
                # thread can safely schedule work (via call_soon_threadsafe) before
                # we block in run_forever().
                asyncio.set_event_loop(loop)
                loop_ready.set()  # Signal that loop is ready for cross-thread use
                loop.run_forever()

            thread = threading.Thread(target=start_loop, daemon=True)
            thread.start()

            # Wait for the loop to be ready before scheduling
            loop_ready.wait()

        # Use thread-safe Event for cross-thread synchronization
        init_done = threading.Event()

        def renew_with_callback():
            try:
                self._renew_token(skip_initial)
            finally:
                init_done.set()

        # Schedule using call_soon_threadsafe for thread-safe scheduling
        self._init_timer = loop.call_soon_threadsafe(renew_with_callback)
        logger.info("Token manager started")

        # Blocks using thread-safe Event
        init_done.wait()
        return self.stop

    async def start_async(
        self,
        listener: CredentialsListener,
        block_for_initial: bool = False,
        initial_delay_in_ms: float = 0,
        skip_initial: bool = False,
    ) -> Callable[[], None]:
        self._listener = listener

        loop = asyncio.get_running_loop()
        init_event = asyncio.Event()

        # Wraps the async callback with async wrapper to schedule with loop.call_later()
        wrapped = _async_to_sync_wrapper(
            loop, self._renew_token_async, skip_initial, init_event