|
| 1 | +From 53da1e8c8ccbe3161ebc42e8b8b7ebd1ab70e05b Mon Sep 17 00:00:00 2001 |
| 2 | +From: "J. Nick Koston" < [email protected]> |
| 3 | +Date: Sun, 18 May 2025 11:56:20 -0400 |
| 4 | +Subject: [PATCH] gh-134173: optimize state transfer between |
| 5 | + `concurrent.futures.Future` and `asyncio.Future` (#134174) |
| 6 | + |
| 7 | +Co-authored-by: Kumar Aditya < [email protected]> |
| 8 | +--- |
| 9 | + Lib/asyncio/futures.py | 17 +++--- |
| 10 | + Lib/concurrent/futures/_base.py | 27 +++++++++ |
| 11 | + Lib/test/test_asyncio/test_futures.py | 58 +++++++++++++++++-- |
| 12 | + .../test_concurrent_futures/test_future.py | 57 ++++++++++++++++++ |
| 13 | + ...-05-18-07-25-15.gh-issue-134173.53oOoF.rst | 3 + |
| 14 | + 5 files changed, 148 insertions(+), 14 deletions(-) |
| 15 | + create mode 100644 Misc/NEWS.d/next/Library/2025-05-18-07-25-15.gh-issue-134173.53oOoF.rst |
| 16 | + |
| 17 | +diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py |
| 18 | +index d1df6707302..6bd00a64478 100644 |
| 19 | +--- a/Lib/asyncio/futures.py |
| 20 | ++++ b/Lib/asyncio/futures.py |
| 21 | +@@ -351,22 +351,19 @@ def _set_concurrent_future_state(concurrent, source): |
| 22 | + def _copy_future_state(source, dest): |
| 23 | + """Internal helper to copy state from another Future. |
| 24 | + |
| 25 | +- The other Future may be a concurrent.futures.Future. |
| 26 | ++ The other Future must be a concurrent.futures.Future. |
| 27 | + """ |
| 28 | +- assert source.done() |
| 29 | + if dest.cancelled(): |
| 30 | + return |
| 31 | + assert not dest.done() |
| 32 | +- if source.cancelled(): |
| 33 | ++ done, cancelled, result, exception = source._get_snapshot() |
| 34 | ++ assert done |
| 35 | ++ if cancelled: |
| 36 | + dest.cancel() |
| 37 | ++ elif exception is not None: |
| 38 | ++ dest.set_exception(_convert_future_exc(exception)) |
| 39 | + else: |
| 40 | +- exception = source.exception() |
| 41 | +- if exception is not None: |
| 42 | +- dest.set_exception(_convert_future_exc(exception)) |
| 43 | +- else: |
| 44 | +- result = source.result() |
| 45 | +- dest.set_result(result) |
| 46 | +- |
| 47 | ++ dest.set_result(result) |
| 48 | + |
| 49 | + def _chain_future(source, destination): |
| 50 | + """Chain two futures so that when one completes, so does the other. |
| 51 | +diff --git a/Lib/concurrent/futures/_base.py b/Lib/concurrent/futures/_base.py |
| 52 | +index d98b1ebdd58..f506ce68aea 100644 |
| 53 | +--- a/Lib/concurrent/futures/_base.py |
| 54 | ++++ b/Lib/concurrent/futures/_base.py |
| 55 | +@@ -558,6 +558,33 @@ def set_exception(self, exception): |
| 56 | + self._condition.notify_all() |
| 57 | + self._invoke_callbacks() |
| 58 | + |
| 59 | ++ def _get_snapshot(self): |
| 60 | ++ """Get a snapshot of the future's current state. |
| 61 | ++ |
| 62 | ++ This method atomically retrieves the state in one lock acquisition, |
| 63 | ++ which is significantly faster than multiple method calls. |
| 64 | ++ |
| 65 | ++ Returns: |
| 66 | ++ Tuple of (done, cancelled, result, exception) |
| 67 | ++ - done: True if the future is done (cancelled or finished) |
| 68 | ++ - cancelled: True if the future was cancelled |
| 69 | ++ - result: The result if available and not cancelled |
| 70 | ++ - exception: The exception if available and not cancelled |
| 71 | ++ """ |
| 72 | ++ # Fast path: check if already finished without lock |
| 73 | ++ if self._state == FINISHED: |
| 74 | ++ return True, False, self._result, self._exception |
| 75 | ++ |
| 76 | ++ # Need lock for other states since they can change |
| 77 | ++ with self._condition: |
| 78 | ++ # We have to check the state again after acquiring the lock |
| 79 | ++ # because it may have changed in the meantime. |
| 80 | ++ if self._state == FINISHED: |
| 81 | ++ return True, False, self._result, self._exception |
| 82 | ++ if self._state in {CANCELLED, CANCELLED_AND_NOTIFIED}: |
| 83 | ++ return True, True, None, None |
| 84 | ++ return False, False, None, None |
| 85 | ++ |
| 86 | + __class_getitem__ = classmethod(types.GenericAlias) |
| 87 | + |
| 88 | + class Executor(object): |
| 89 | + |
0 commit comments