Skip to content

Commit

Permalink
chore: Update Client.close to prevent double closing and race conditions
Browse files Browse the repository at this point in the history
  • Loading branch information
DA-344 committed Dec 11, 2024
1 parent f5f0085 commit 28fab35
Showing 1 changed file with 19 additions and 16 deletions.
35 changes: 19 additions & 16 deletions discord/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,8 @@ def __init__(
self._enable_debug_events: bool = options.pop("enable_debug_events", False)
self._connection: ConnectionState = self._get_state(**options)
self._connection.shard_count = self.shard_count
self._closed: bool = False
self._closed: asyncio.Event = asyncio.Event()
self._closing_task: asyncio.Lock = asyncio.Lock()
self._ready: asyncio.Event = asyncio.Event()
self._connection._get_websocket = self._get_websocket
self._connection._get_client = lambda: self
Expand Down Expand Up @@ -289,6 +290,7 @@ async def __aenter__(self) -> Client:
self._connection.loop = self.loop

self._ready = asyncio.Event()
self._closed = asyncio.Event()

return self

Expand Down Expand Up @@ -725,23 +727,24 @@ async def close(self) -> None:
Closes the connection to Discord.
"""
if self._closed:
if self.is_closed():
return

await self.http.close()
self._closed = True
async with self._closing_task:
await self.http.close()

for voice in self.voice_clients:
try:
await voice.disconnect(force=True)
except Exception:
# if an error happens during disconnects, disregard it.
pass
for voice in self.voice_clients:
try:
await voice.disconnect(force=True)
except Exception:
# if an error happens during disconnects, disregard it.
pass

if self.ws is not None and self.ws.open:
await self.ws.close(code=1000)
if self.ws is not None and self.ws.open:
await self.ws.close(code=1000)

self._ready.clear()
self._ready.clear()
self._closed.set()

def clear(self) -> None:
"""Clears the internal state of the bot.
Expand Down Expand Up @@ -818,14 +821,14 @@ async def runner():
if not self.is_closed():
self.loop.run_until_complete(self.close())

_log.info("Cleaning up tasks.")
_cleanup_loop(self.loop)
_log.info("Cleaning up tasks.")
_cleanup_loop(self.loop)

# properties

def is_closed(self) -> bool:
"""Indicates if the WebSocket connection is closed."""
return self._closed
return self._closed.is_set()

@property
def activity(self) -> ActivityTypes | None:
Expand Down

0 comments on commit 28fab35

Please sign in to comment.