Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 10 additions & 43 deletions py_hamt/store.py
Original file line number Diff line number Diff line change
Expand Up @@ -238,53 +238,20 @@ def _loop_session(self) -> aiohttp.ClientSession:
# graceful shutdown: close **all** sessions we own #
# --------------------------------------------------------------------- #
async def aclose(self) -> None:
"""
Close all sessions we own, properly handling multi-loop scenarios.

- Sessions in the current loop are properly awaited and closed
- Sessions in other loops are removed from tracking (can't be safely closed from here)
- All references are cleaned up to allow garbage collection
"""
"""Close all internally-created sessions."""
if not self._owns_session:
# User provided the session, they're responsible for closing it
# User supplied the session; they are responsible for closing it.
return

# Get the current event loop if one exists
current_loop = None
try:
current_loop = asyncio.get_running_loop()
except RuntimeError:
# No running event loop - just clear all references
# The sessions will be garbage collected eventually
self._session_per_loop.clear()
return
for sess in list(self._session_per_loop.values()):
if not sess.closed:
try:
await sess.close()
except Exception:
# Best-effort cleanup; ignore errors during shutdown
pass

# Separate sessions by whether they belong to the current loop
sessions_to_close = []
loops_to_remove = []

for loop, sess in list(self._session_per_loop.items()):
if loop == current_loop:
# This session belongs to our current loop
if not sess.closed:
sessions_to_close.append(sess)
loops_to_remove.append(loop)
else:
# Session belongs to a different loop
# We can't safely close it from here, but we should remove our reference
loops_to_remove.append(loop)

# Close all sessions that belong to the current loop
for sess in sessions_to_close:
try:
await sess.close()
except Exception:
# If closing fails, continue anyway
pass

# Remove all references (both current loop and other loops)
for loop in loops_to_remove:
self._session_per_loop.pop(loop, None)
self._session_per_loop.clear()

# At this point, _session_per_loop should be empty or only contain
# sessions from loops we haven't seen (which shouldn't happen in practice)
Expand Down
3 changes: 1 addition & 2 deletions tests/test_kubocas_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,5 +98,4 @@ async def test_distinct_loops_get_distinct_sessions():

# Clean‑up
await kubo.aclose()
if not secondary_session.closed:
await asyncio.to_thread(secondary_session.close)
assert secondary_session.closed
Loading