Skip to content

Commit dd5281f

Browse files
committed
Switch from raising a timeout on EINTR to retrying until the timeout has elapsed
Earlier I had submitted #250 to handle select being interrupted, and had changed this behavior to return as if it was a timeout. Now that we've been running this for a while we are seeing some occasional issues with that patch. Primarily that the remaining kazoo codebase assumes that a timeout from select is an actual timeout (which is reasonable)-- except that the previous patch changed that-- so you actually have to check the time elapsed in addition to the return. Instead of doing that (which seems like a mess, and error prone) this patch simply retries until the given timeout (assuming one was given). This does take into account that you may experience more than one interrupt in a given select() call (and we adjust the timeout accordingly for each iteration).
1 parent f055dc0 commit dd5281f

1 file changed

Lines changed: 18 additions & 11 deletions

File tree

kazoo/handlers/threading.py

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -143,17 +143,24 @@ def stop(self):
143143
python2atexit.unregister(self.stop)
144144

145145
def select(self, *args, **kwargs):
146-
try:
147-
return select.select(*args, **kwargs)
148-
except select.error as ex:
149-
# if the system call was interrupted, we'll return as a timeout
150-
# in Python 3, system call interruptions are a native exception
151-
# in Python 2, they are not
152-
errnum = ex.errno if isinstance(ex, OSError) else ex[0]
153-
# to mimic a timeout, we return the same thing select would
154-
if errnum == errno.EINTR:
155-
return ([], [], [])
156-
raise
146+
# select() takes no kwargs, so it will be in args
147+
timeout = args[3] if len(args) == 4 else None
148+
# either the time to give up, or None
149+
end = (time.time() + timeout) if timeout else None
150+
while timeout is None or time.time() < end:
151+
if timeout:
152+
args = list(args) # make a list, since tuples aren't mutable
153+
args[3] = end - time.time() # set the timeout to the remaining time
154+
try:
155+
return select.select(*args, **kwargs)
156+
except select.error as ex:
157+
# if the system call was interrupted, we'll retry until timeout
158+
# in Python 3, system call interruptions are a native exception
159+
# in Python 2, they are not
160+
errnum = ex.errno if isinstance(ex, OSError) else ex[0]
161+
if errnum == errno.EINTR:
162+
continue
163+
raise
157164

158165
def socket(self):
159166
return utils.create_tcp_socket(socket)

0 commit comments

Comments
 (0)