Skip to content

Commit dc84582

Browse files
committed
Fix bug with async_lock implementation
- ``async_lock`` would hold onto the lock even if a task had been cancelled. This change ensures that the lock will be released if an exception is thrown during ``loop.run_in_executor()``.
1 parent a59bcb3 commit dc84582

File tree

3 files changed

+24
-1
lines changed

3 files changed

+24
-1
lines changed

newsfragments/2695.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Properly release ``async_lock`` for session requests if an exception is raised during a task.

tests/core/utilities/test_request.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
)
3030
from web3._utils.request import (
3131
SessionCache,
32+
_async_session_cache_lock,
33+
async_lock,
3234
cache_and_return_async_session,
3335
cache_and_return_session,
3436
)
@@ -323,3 +325,23 @@ def target_function(endpoint_uri):
323325

324326
# clear cache
325327
request._async_session_cache.clear()
328+
329+
330+
@pytest.mark.asyncio
331+
async def test_async_lock_releases_if_a_task_is_cancelled():
332+
# inspired by issue #2693
333+
# Note: this test will raise a `TimeoutError` if `request.async_lock` is not
334+
# applied correctly
335+
336+
async def _utilize_async_lock():
337+
async with async_lock(_async_session_cache_lock):
338+
await asyncio.sleep(0.1)
339+
340+
asyncio.create_task(_utilize_async_lock())
341+
342+
inner = asyncio.create_task(_utilize_async_lock())
343+
await asyncio.sleep(0.1)
344+
inner.cancel()
345+
346+
outer = asyncio.wait_for(_utilize_async_lock(), 1)
347+
await outer

web3/_utils/request.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,8 @@ async def cache_and_return_async_session(
196196
@contextlib.asynccontextmanager
197197
async def async_lock(lock: threading.Lock) -> AsyncGenerator[None, None]:
198198
loop = asyncio.get_event_loop()
199-
await loop.run_in_executor(_pool, lock.acquire)
200199
try:
200+
await loop.run_in_executor(_pool, lock.acquire)
201201
yield
202202
finally:
203203
lock.release()

0 commit comments

Comments
 (0)