]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-104144: Optimize gather to finish eagerly when all futures complete eagerly (...
authorItamar Ostricher <itamarost@gmail.com>
Sat, 6 May 2023 15:15:27 +0000 (08:15 -0700)
committerGitHub <noreply@github.com>
Sat, 6 May 2023 15:15:27 +0000 (15:15 +0000)
Lib/asyncio/tasks.py
Misc/NEWS.d/next/Library/2023-05-03-16-50-24.gh-issue-104144.yNkjL8.rst [new file with mode: 0644]

index aa5269ade19a7fdcee25895f8d95b52f64ff235f..7eb647bd129819179747620f9232ee89fe1c3aea 100644 (file)
@@ -813,6 +813,7 @@ def gather(*coros_or_futures, return_exceptions=False):
     children = []
     nfuts = 0
     nfinished = 0
+    done_futs = []
     loop = None
     outer = None  # bpo-46672
     for arg in coros_or_futures:
@@ -829,7 +830,10 @@ def gather(*coros_or_futures, return_exceptions=False):
 
             nfuts += 1
             arg_to_fut[arg] = fut
-            fut.add_done_callback(_done_callback)
+            if fut.done():
+                done_futs.append(fut)
+            else:
+                fut.add_done_callback(_done_callback)
 
         else:
             # There's a duplicate Future object in coros_or_futures.
@@ -838,6 +842,13 @@ def gather(*coros_or_futures, return_exceptions=False):
         children.append(fut)
 
     outer = _GatheringFuture(children, loop=loop)
+    # Run done callbacks after GatheringFuture created so any post-processing
+    # can be performed at this point
+    # optimization: in the special case that *all* futures finished eagerly,
+    # this will effectively complete the gather eagerly, with the last
+    # callback setting the result (or exception) on outer before returning it
+    for fut in done_futs:
+        _done_callback(fut)
     return outer
 
 
diff --git a/Misc/NEWS.d/next/Library/2023-05-03-16-50-24.gh-issue-104144.yNkjL8.rst b/Misc/NEWS.d/next/Library/2023-05-03-16-50-24.gh-issue-104144.yNkjL8.rst
new file mode 100644 (file)
index 0000000..b975d48
--- /dev/null
@@ -0,0 +1,3 @@
+Optimize :func:`asyncio.gather` when using :func:`asyncio.eager_task_factory`
+to complete eagerly if all fututres completed eagerly.
+Avoid scheduling done callbacks for futures that complete eagerly.