diff --git a/changelogs/fragments/fix-selectors-error-condition.yaml b/changelogs/fragments/fix-selectors-error-condition.yaml new file mode 100644 index 00000000000..7ce618dde8b --- /dev/null +++ b/changelogs/fragments/fix-selectors-error-condition.yaml @@ -0,0 +1,5 @@ +--- +bugfixes: +- fix for the bundled selectors module (used in the ssh and local connection + plugins) when a syscall is restarted after being interrupted by a signal + (https://github.com/ansible/ansible/issues/41630) diff --git a/lib/ansible/compat/selectors/__init__.py b/lib/ansible/compat/selectors/__init__.py index 6458438d6dc..8d45ea5fbff 100644 --- a/lib/ansible/compat/selectors/__init__.py +++ b/lib/ansible/compat/selectors/__init__.py @@ -25,6 +25,14 @@ package exists on pypi to backport the functionality as far as python-2.6. ''' # The following makes it easier for us to script updates of the bundled code _BUNDLED_METADATA = {"pypi_name": "selectors2", "version": "1.1.0"} +# Added these bugfix commits from 2.1.0: +# * https://github.com/SethMichaelLarson/selectors2/commit/3bd74f2033363b606e1e849528ccaa76f5067590 +# Wrap kqueue.control so that timeout is a keyword arg +# * https://github.com/SethMichaelLarson/selectors2/commit/6f6a26f42086d8aab273b30be492beecb373646b +# Fix formatting of the kqueue.control patch for pylint +# * https://github.com/SethMichaelLarson/selectors2/commit/f0c2c6c66cfa7662bc52beaf4e2d65adfa25e189 +# Fix use of OSError exception for py3 and use the wrapper of kqueue.control so retries of +# interrupted syscalls work with kqueue import os.path import sys diff --git a/lib/ansible/compat/selectors/_selectors2.py b/lib/ansible/compat/selectors/_selectors2.py index a97b59e6630..bb5142c14cf 100644 --- a/lib/ansible/compat/selectors/_selectors2.py +++ b/lib/ansible/compat/selectors/_selectors2.py @@ -136,7 +136,7 @@ else: if expires is not None: current_time = monotonic() if current_time > expires: - raise OSError(errno=errno.ETIMEDOUT) + raise OSError(errno.ETIMEDOUT) if recalc_timeout: if "timeout" in kwargs: kwargs["timeout"] = expires - current_time @@ -340,7 +340,7 @@ if hasattr(select, "select"): timeout = None if timeout is None else max(timeout, 0.0) ready = [] r, w, _ = _syscall_wrapper(self._select, True, self._readers, - self._writers, timeout) + self._writers, timeout=timeout) r = set(r) w = set(w) for fd in r | w: @@ -561,14 +561,14 @@ if hasattr(select, "kqueue"): select.KQ_FILTER_READ, select.KQ_EV_ADD) - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) + _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0) if events & EVENT_WRITE: kevent = select.kevent(key.fd, select.KQ_FILTER_WRITE, select.KQ_EV_ADD) - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) + _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0) return key @@ -579,7 +579,7 @@ if hasattr(select, "kqueue"): select.KQ_FILTER_READ, select.KQ_EV_DELETE) try: - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) + _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0) except SelectorError: pass if key.events & EVENT_WRITE: @@ -587,7 +587,7 @@ if hasattr(select, "kqueue"): select.KQ_FILTER_WRITE, select.KQ_EV_DELETE) try: - _syscall_wrapper(self._kqueue.control, False, [kevent], 0, 0) + _syscall_wrapper(self._wrap_control, False, [kevent], 0, 0) except SelectorError: pass @@ -600,8 +600,8 @@ if hasattr(select, "kqueue"): max_events = len(self._fd_to_key) * 2 ready_fds = {} - kevent_list = _syscall_wrapper(self._kqueue.control, True, - None, max_events, timeout) + kevent_list = _syscall_wrapper(self._wrap_control, True, + None, max_events, timeout=timeout) for kevent in kevent_list: fd = kevent.ident @@ -626,6 +626,9 @@ if hasattr(select, "kqueue"): self._kqueue.close() super(KqueueSelector, self).close() + def _wrap_control(self, changelist, max_events, timeout): + return self._kqueue.control(changelist, max_events, timeout) + __all__.append('KqueueSelector')