You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
If I make a number of GET requests in concurrent threads using a shared httpcore.ConnectionPool (in my case, via httpx), I reliably hit this assertion failure.
❯ python httpx_race.pyTraceback (most recent call last): File "/Users/nelhage/code/sandbox/httpx_race.py", line 20, in <module> main() File "/Users/nelhage/code/sandbox/httpx_race.py", line 16, in main list(tpe.map(get_one, range(n_ops))) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/concurrent/futures/_base.py", line 621, in result_iterator yield _result_or_cancel(fs.pop()) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/concurrent/futures/_base.py", line 319, in _result_or_cancel return fut.result(timeout) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/concurrent/futures/_base.py", line 451, in result return self.__get_result() File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/concurrent/futures/_base.py", line 403, in __get_result raise self._exception File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/concurrent/futures/thread.py", line 58, in run result = self.fn(*self.args, **self.kwargs) File "/Users/nelhage/code/sandbox/httpx_race.py", line 8, in get_one http_client.get("https://example.com/") File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_client.py", line 1039, in get return self.request( File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_client.py", line 815, in request return self.send(request, auth=auth, follow_redirects=follow_redirects) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_client.py", line 902, in send response = self._send_handling_auth( File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_client.py", line 930, in _send_handling_auth response = self._send_handling_redirects( File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_client.py", line 967, in _send_handling_redirects response = self._send_single_request(request) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_client.py", line 1003, in _send_single_request response = transport.handle_request(request) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpx/_transports/default.py", line 218, in handle_request resp = self._pool.handle_request(req) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 221, in handle_request self._attempt_to_acquire_connection(status) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 160, in _attempt_to_acquire_connection status.set_connection(connection) File "/Users/nelhage/miniforge3/envs/py310/lib/python3.10/site-packages/httpcore/_sync/connection_pool.py", line 22, in set_connection assert self.connection is NoneAssertionError
As soon as we add status to self._requests outside of the lock (line 217), it becomes visible to other threads. One of those threads may complete a request, and may assign us a connection:
We will then acquire the lock and enter _attempt_to_acquire_connection , which will also attempt to assign a connection.
An immediate fix would be to move line 217 inside of the lock, but _requests is accessed outside of the lock in various other places so I'm not sure that this would be sufficient or the best fix.
The text was updated successfully, but these errors were encountered:
Note that the proximate cause noted above is a sufficient but not necessary condition - we first encountered thread-safety problems in version 0.15, before#580 moved self._requests.append(status) outside the lock.
If I make a number of
GET
requests in concurrent threads using a sharedhttpcore.ConnectionPool
(in my case, viahttpx
), I reliably hit this assertion failure.Test script:
Output:
The proximate issue appears to be in these lines:
httpcore/httpcore/_sync/connection_pool.py
Lines 216 to 221 in 2f1cf70
As soon as we add
status
toself._requests
outside of the lock (line 217), it becomes visible to other threads. One of those threads may complete a request, and may assign us a connection:httpcore/httpcore/_sync/connection_pool.py
Lines 287 to 289 in 2f1cf70
We will then acquire the lock and enter
_attempt_to_acquire_connection
, which will also attempt to assign a connection.An immediate fix would be to move line 217 inside of the lock, but
_requests
is accessed outside of the lock in various other places so I'm not sure that this would be sufficient or the best fix.The text was updated successfully, but these errors were encountered: