2
0
mirror of https://opendev.org/x/pyghmi synced 2025-01-27 19:37:44 +00:00

Replace waitall mechanism with never wait

Previously waitall was used as a way to have wait_for_rsp be able to
call out for specifically keepalive, to prevent a wait_for_rsp from
hanging.  The thought was to create a new wait_for_rsp by recursing.
However, if things go really bad with enough systems, it runs afoul of
python's recursion limit.  The alternative strategy is to provide a way
to never wait, and asynchronously do cleanup in a callback.  This
returns to the original wait_for_rsp instance rather than recursing
to a new one, keeping the stack shallow.

Change-Id: I54450bfc5bf57563a0a8bd986d9aea887bf36e53
This commit is contained in:
Jarrod Johnson 2017-08-02 15:32:12 -04:00
parent 19ae81f1dc
commit 56e3eb5cb7

View File

@ -707,20 +707,14 @@ class Session(object):
incrementtime += 1
return cumulativetime + 1
def _cmdwait(self, waitall=False):
def _cmdwait(self):
while self._isincommand():
if waitall:
Session.wait_for_rsp(self._isincommand())
else:
_io_wait(self._isincommand(), self.sockaddr, self.evq)
_io_wait(self._isincommand(), self.sockaddr, self.evq)
def awaitresponse(self, retry, waitall=False):
def awaitresponse(self, retry):
while retry and self.lastresponse is None and self.logged:
timeout = self.expiration - _monotonic_time()
if waitall:
Session.wait_for_rsp(timeout)
else:
_io_wait(timeout, self.sockaddr)
_io_wait(timeout, self.sockaddr)
while self.iterwaiters:
waiter = self.iterwaiters.pop()
waiter({'success': True})
@ -739,15 +733,18 @@ class Session(object):
retry=True,
delay_xmit=None,
timeout=None,
waitall=False):
callback=None):
if not self.logged:
raise exc.IpmiException('Session no longer connected')
self._cmdwait(waitall)
self._cmdwait()
if not self.logged:
raise exc.IpmiException('Session no longer connected')
self.incommand = _monotonic_time() + self._getmaxtimeout()
self.lastresponse = None
self.ipmicallback = self._generic_callback
if callback is None:
self.ipmicallback = self._generic_callback
else:
self.ipmicallback = callback
self._send_ipmi_net_payload(netfn, command, data,
bridge_request=bridge_request,
retry=retry, delay_xmit=delay_xmit,
@ -757,13 +754,16 @@ class Session(object):
timeout = None
else: # if not retry, give it a second before surrending
timeout = 1
if callback:
# caller *must* clean up self.incommand and self.evq
return
# The event loop is shared amongst pyghmi session instances
# within a process. In this way, synchronous usage of the interface
# plays well with asynchronous use. In fact, this produces the
# behavior of only the constructor needing a callback. From then on,
# synchronous usage of the class acts in a greenthread style governed
# by order of data on the network
self.awaitresponse(retry, waitall)
self.awaitresponse(retry)
lastresponse = self.lastresponse
self.incommand = False
while self.evq:
@ -1194,6 +1194,20 @@ class Session(object):
except KeyError:
pass
def _keepalive_wrapper(self, callback):
# generates a wrapped keepalive to cleanup session state
# and call callback if appropriate
def _keptalive(response):
self._generic_callback(response)
response = self.lastresponse
self.incommand = False
while self.evq:
self.evq.popleft().set()
if callback:
callback(response)
return _keptalive
def _keepalive(self):
"""Performs a keepalive to avoid idle disconnect
"""
@ -1202,7 +1216,8 @@ class Session(object):
if self.incommand:
# if currently in command, no cause to keepalive
return
self.raw_command(netfn=6, command=1, waitall=True)
self.raw_command(netfn=6, command=1,
callback=self._keepalive_wrapper(None))
else:
kaids = list(self._customkeepalives.keys())
for keepalive in kaids:
@ -1215,8 +1230,8 @@ class Session(object):
# raw command ultimately caused a keepalive to
# deregister
continue
cmd['waitall'] = True
callback(self.raw_command(**cmd))
cmd['callback'] = self._keepalive_wrapper(callback)
self.raw_command(**cmd)
except exc.IpmiException:
self._mark_broken()