Closed as not planned
Description
The alternative is using the fast C atomic
compare_and_swap
function
which is executed under the hold GIL.
We need the pure-Python fallback anyway.
I was checking some minor detail in socketpair and noticed this:
Line 700 in bceb197
and it looks like it benchmarks a 100ns faster than a global lock:
#!/usr/bin/env python3
import pyperf
from asyncio import events
"""Event loop mixins."""
import threading
_global_lock = threading.Lock()
# Used as a sentinel for loop parameter
_marker = object()
class _LoopBoundMixin:
_loop = None
def __init__(self, *, loop=_marker):
if loop is not _marker:
raise TypeError(
f'As of 3.10, the *loop* parameter was removed from '
f'{type(self).__name__}() since it is no longer necessary'
)
def _get_loop(self):
loop = events._get_running_loop()
if self._loop is None:
with _global_lock:
if self._loop is None:
self._loop = loop
if loop is not self._loop:
raise RuntimeError(f'{self!r} is bound to a different event loop')
return loop
class _LoopBoundMixinSetDefault:
_loop = None
def __init__(self, *, loop=_marker):
if loop is not _marker:
raise TypeError(
f'As of 3.10, the *loop* parameter was removed from '
f'{type(self).__name__}() since it is no longer necessary'
)
def _get_loop(self):
loop = events._get_running_loop()
if self._loop is None:
self.__dict__.setdefault("_loop", loop)
if self._loop is not loop:
raise RuntimeError(f'{self!r} is bound to a different event loop')
return loop
runner = pyperf.Runner()
runner.timeit(name="get loop with lock",
stmt="lbm = _LoopBoundMixin(); lbm._get_loop(); lbm._get_loop()",
setup="import asyncio; from __main__ import _LoopBoundMixin; asyncio._set_running_loop(asyncio.new_event_loop())")
runner.timeit(name="get loop with setdefault",
stmt="lbm = _LoopBoundMixin(); lbm._get_loop(); lbm._get_loop()",
setup="import asyncio; from __main__ import _LoopBoundMixinSetDefault as _LoopBoundMixin; asyncio._set_running_loop(asyncio.new_event_loop())")
runner.timeit(name="get loop already set with lock",
stmt="lbm._get_loop()",
setup="import asyncio; from __main__ import _LoopBoundMixin; asyncio._set_running_loop(asyncio.new_event_loop()); lbm = _LoopBoundMixin(); lbm._get_loop()")
runner.timeit(name="get loop already set with setdefault",
stmt="lbm._get_loop()",
setup="import asyncio; from __main__ import _LoopBoundMixinSetDefault as _LoopBoundMixin; asyncio._set_running_loop(asyncio.new_event_loop()); lbm = _LoopBoundMixin(); lbm._get_loop()")
Originally posted by @graingert in #86558 (comment)