Skip to content

Commit 0f34e25

Browse files
authored
Merge pull request #1065 from moreati/issue957
ansible_mitogen: Fix "filedescriptor out of range in select()" in WorkerProcess
2 parents bb9c51b + 4996ec2 commit 0f34e25

File tree

5 files changed

+26
-43
lines changed

5 files changed

+26
-43
lines changed

ansible_mitogen/process.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -281,11 +281,11 @@ def get_cpu_count(default=None):
281281

282282
class Broker(mitogen.master.Broker):
283283
"""
284-
WorkerProcess maintains at most 2 file descriptors, therefore does not need
284+
WorkerProcess maintains fewer file descriptors, therefore does not need
285285
the exuberant syscall expense of EpollPoller, so override it and restore
286286
the poll() poller.
287287
"""
288-
poller_class = mitogen.core.Poller
288+
poller_class = mitogen.parent.POLLER_LIGHTWEIGHT
289289

290290

291291
class Binding(object):

docs/changelog.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ Unreleased
2222
----------
2323

2424
* :gh:issue:`952` Fix Ansible `--ask-become-pass`, add test coverage
25+
* :gh:issue:`957` Fix Ansible exception when executing against 10s of hosts
26+
"ValueError: filedescriptor out of range in select()"
2527

2628

2729
v0.3.7 (2024-04-08)

docs/contributors.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,13 @@ sponsorship and outstanding future-thinking of its early adopters.
126126
<li>jgadling</li>
127127
<li>John F Wall &mdash; <em>Making Ansible Great with Massive Parallelism</em></li>
128128
<li>KennethC</li>
129+
<li><a href="https://github.com/lberruti">Luca Berruti</li>
129130
<li>Lewis Bellwood &mdash; <em>Happy to be apart of a great project.</em></li>
130131
<li>luto</li>
131132
<li><a href="https://mayeu.me/">Mayeu a.k.a Matthieu Maury</a></li>
132133
<li><a href="https://twitter.com/nathanhruby">@nathanhruby</a></li>
133134
<li><a href="https://github.com/opoplawski">Orion Poplawski</a></li>
135+
<li><a href="https://github.com/philfry">Philippe Kueck</a></li>
134136
<li><a href="http://pageflows.com/">Ramy</a></li>
135137
<li>Scott Vokes</li>
136138
<li><a href="https://twitter.com/sirtux">Tom Eichhorn</a></li>

mitogen/core.py

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2523,8 +2523,7 @@ class Poller(object):
25232523
"""
25242524
A poller manages OS file descriptors the user is waiting to become
25252525
available for IO. The :meth:`poll` method blocks the calling thread
2526-
until one or more become ready. The default implementation is based on
2527-
:func:`select.poll`.
2526+
until one or more become ready.
25282527
25292528
Each descriptor has an associated `data` element, which is unique for each
25302529
readiness type, and defaults to being the same as the file descriptor. The
@@ -2546,19 +2545,13 @@ class Poller(object):
25462545
a resource leak.
25472546
25482547
Pollers may only be used by one thread at a time.
2548+
2549+
This implementation uses :func:`select.select` for wider platform support.
2550+
That is considered an implementation detail. Previous versions have used
2551+
:func:`select.poll`. Future versions may decide at runtime.
25492552
"""
25502553
SUPPORTED = True
25512554

2552-
# This changed from select() to poll() in Mitogen 0.2.4. Since poll() has
2553-
# no upper FD limit, it is suitable for use with Latch, which must handle
2554-
# FDs larger than select's limit during many-host runs. We want this
2555-
# because poll() requires no setup and teardown: just a single system call,
2556-
# which is important because Latch.get() creates a Poller on each
2557-
# invocation. In a microbenchmark, poll() vs. epoll_ctl() is 30% faster in
2558-
# this scenario. If select() must return in future, it is important
2559-
# Latch.poller_class is set from parent.py to point to the industrial
2560-
# strength poller for the OS, otherwise Latch will fail randomly.
2561-
25622555
#: Increments on every poll(). Used to version _rfds and _wfds.
25632556
_generation = 1
25642557

@@ -2681,11 +2674,10 @@ class Latch(object):
26812674
26822675
See :ref:`waking-sleeping-threads` for further discussion.
26832676
"""
2684-
#: The :class:`Poller` implementation to use for waiting. Since the poller
2685-
#: will be very short-lived, we prefer :class:`mitogen.parent.PollPoller`
2686-
#: if it is available, or :class:`mitogen.core.Poller` otherwise, since
2687-
#: these implementations require no system calls to create, configure or
2688-
#: destroy.
2677+
#: The :class:`Poller` implementation to use. Instances are short lived so
2678+
#: prefer :class:`mitogen.parent.PollPoller` if it's available, otherwise
2679+
#: :class:`mitogen.core.Poller`. They don't need syscalls to create,
2680+
#: configure, or destroy. Replaced during import of :mod:`mitogen.parent`.
26892681
poller_class = Poller
26902682

26912683
#: If not :data:`None`, a function invoked as `notify(latch)` after a

mitogen/parent.py

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -745,8 +745,7 @@ def _upgrade_broker(broker):
745745
broker.timers = TimerList()
746746
LOG.debug('upgraded %r with %r (new: %d readers, %d writers; '
747747
'old: %d readers, %d writers)', old, new,
748-
len(new.readers), len(new.writers),
749-
len(old.readers), len(old.writers))
748+
len(new._rfds), len(new._wfds), len(old._rfds), len(old._wfds))
750749

751750

752751
@mitogen.core.takes_econtext
@@ -902,22 +901,18 @@ def __repr__(self):
902901
class PollPoller(mitogen.core.Poller):
903902
"""
904903
Poller based on the POSIX :linux:man2:`poll` interface. Not available on
905-
some versions of OS X, otherwise it is the preferred poller for small FD
906-
counts, as there is no setup/teardown/configuration system call overhead.
904+
some Python/OS X combinations. Otherwise the preferred poller for small
905+
FD counts; or if many pollers are created, used once, then closed.
906+
There there is no setup/teardown/configuration system call overhead.
907907
"""
908908
SUPPORTED = hasattr(select, 'poll')
909-
_repr = 'PollPoller()'
909+
_readmask = SUPPORTED and select.POLLIN | select.POLLHUP
910910

911911
def __init__(self):
912912
super(PollPoller, self).__init__()
913913
self._pollobj = select.poll()
914914

915915
# TODO: no proof we dont need writemask too
916-
_readmask = (
917-
getattr(select, 'POLLIN', 0) |
918-
getattr(select, 'POLLHUP', 0)
919-
)
920-
921916
def _update(self, fd):
922917
mask = (((fd in self._rfds) and self._readmask) |
923918
((fd in self._wfds) and select.POLLOUT))
@@ -952,7 +947,6 @@ class KqueuePoller(mitogen.core.Poller):
952947
Poller based on the FreeBSD/Darwin :freebsd:man2:`kqueue` interface.
953948
"""
954949
SUPPORTED = hasattr(select, 'kqueue')
955-
_repr = 'KqueuePoller()'
956950

957951
def __init__(self):
958952
super(KqueuePoller, self).__init__()
@@ -1030,7 +1024,7 @@ class EpollPoller(mitogen.core.Poller):
10301024
Poller based on the Linux :linux:man7:`epoll` interface.
10311025
"""
10321026
SUPPORTED = hasattr(select, 'epoll')
1033-
_repr = 'EpollPoller()'
1027+
_inmask = SUPPORTED and select.EPOLLIN | select.EPOLLHUP
10341028

10351029
def __init__(self):
10361030
super(EpollPoller, self).__init__()
@@ -1077,9 +1071,6 @@ def stop_transmit(self, fd):
10771071
self._wfds.pop(fd, None)
10781072
self._control(fd)
10791073

1080-
_inmask = (getattr(select, 'EPOLLIN', 0) |
1081-
getattr(select, 'EPOLLHUP', 0))
1082-
10831074
def _poll(self, timeout):
10841075
the_timeout = -1
10851076
if timeout is not None:
@@ -1100,18 +1091,14 @@ def _poll(self, timeout):
11001091
yield data
11011092

11021093

1103-
# 2.4 and 2.5 only had select.select() and select.poll().
1104-
for _klass in mitogen.core.Poller, PollPoller, KqueuePoller, EpollPoller:
1105-
if _klass.SUPPORTED:
1106-
PREFERRED_POLLER = _klass
1094+
POLLERS = (EpollPoller, KqueuePoller, PollPoller, mitogen.core.Poller)
1095+
PREFERRED_POLLER = next(cls for cls in POLLERS if cls.SUPPORTED)
1096+
11071097

11081098
# For processes that start many threads or connections, it's possible Latch
11091099
# will also get high-numbered FDs, and so select() becomes useless there too.
1110-
# So swap in our favourite poller.
1111-
if PollPoller.SUPPORTED:
1112-
mitogen.core.Latch.poller_class = PollPoller
1113-
else:
1114-
mitogen.core.Latch.poller_class = PREFERRED_POLLER
1100+
POLLER_LIGHTWEIGHT = PollPoller.SUPPORTED and PollPoller or PREFERRED_POLLER
1101+
mitogen.core.Latch.poller_class = POLLER_LIGHTWEIGHT
11151102

11161103

11171104
class LineLoggingProtocolMixin(object):

0 commit comments

Comments
 (0)