@@ -20,11 +20,25 @@ from libcpp.memory cimport make_unique, unique_ptr
2020from cython.operator cimport dereference as deref
2121from weakref import WeakKeyDictionary
2222from cpython.ref cimport PyObject
23+ from cpython.weakref cimport PyWeakref_NewRef, PyWeakref_GetObject
2324
2425# asyncio Loops to AsyncioExecutor
2526loop_to_q = WeakKeyDictionary()
2627_RaiseKeyError = object ()
2728
29+ # weak reference to the last seen event loop
30+ _last_loop = None
31+ # AsyncioExecutor for the last seen event loop
32+ _last_executor = None
33+
34+
35+ # cleanup callback that clears _last_loop and _last_executor when the loop
36+ # referenced by _last_loop is collected
37+ def _clean_last_executor (_wr ):
38+ global _last_loop, _last_executor
39+ _last_loop = None
40+ _last_executor = None
41+
2842
2943cdef class AsyncioExecutor:
3044 def __cinit__ (self ):
@@ -112,28 +126,41 @@ cdef cAsyncioExecutor* get_running_executor(bint running) noexcept:
112126
113127cdef cAsyncioExecutor* get_running_executor_drive(
114128 bint running, bint driveBeforeDealloc) noexcept:
129+ global _last_loop, _last_executor
115130 try :
116131 if running:
117132 loop = asyncio.get_running_loop()
118133 else :
119134 loop = asyncio.get_event_loop()
120135 except RuntimeError :
121136 return NULL
122- try :
123- executor = < AsyncioExecutor> (loop_to_q[loop])
124- except KeyError :
125- if sys.platform == " win32" :
126- executor = ProactorExecutor(loop._proactor._iocp)
127- queue = IocpQueue(executor)
128- queue.swap(loop)
129- else :
130- executor = NotificationQueueAsyncioExecutor(driveBeforeDealloc)
131- loop.add_reader(executor.fileno(), executor.drive)
132- loop_to_q[loop] = executor
137+ cdef AsyncioExecutor executor = None
138+ if _last_loop is not None and < PyObject* > loop is PyWeakref_GetObject(_last_loop):
139+ executor = _last_executor
140+ if executor is None :
141+ try :
142+ executor = < AsyncioExecutor> (loop_to_q[loop])
143+ except KeyError :
144+ if sys.platform == " win32" :
145+ proactor = ProactorExecutor(loop._proactor._iocp)
146+ queue = IocpQueue(proactor)
147+ queue.swap(loop)
148+ executor = proactor
149+ else :
150+ nq = NotificationQueueAsyncioExecutor(driveBeforeDealloc)
151+ loop.add_reader(nq.fileno(), nq.drive)
152+ executor = nq
153+ loop_to_q[loop] = executor
154+ _last_loop = PyWeakref_NewRef(loop, _clean_last_executor)
155+ _last_executor = executor
133156 return executor._executor
134157
135158
136159cdef int set_executor_for_loop(object loop, cAsyncioExecutor* c_executor) noexcept:
160+ global _last_loop, _last_executor
161+ # drop the fast-path cache to avoid returning a stale executor
162+ _last_loop = None
163+ _last_executor = None
137164 if c_executor == NULL :
138165 del loop_to_q[loop]
139166 return 0
0 commit comments