From 1f997dd9b8e6ce5e459c0f1a869c8c5e8ff4de97 Mon Sep 17 00:00:00 2001 From: Bastian Krause Date: Thu, 16 Jun 2022 00:40:50 +0200 Subject: [PATCH 1/2] util/timeout: allow zero timeout In certain situations it makes sense to specify a timeout of 0, e.g. if no blocking sleeps are desired in a method that awaits some condition. Let's add an easy way to allow an instant timeout by considering zero a valid value for timeouts. Signed-off-by: Bastian Krause --- labgrid/util/timeout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/labgrid/util/timeout.py b/labgrid/util/timeout.py index 04dda1b98..d1d792efa 100644 --- a/labgrid/util/timeout.py +++ b/labgrid/util/timeout.py @@ -11,7 +11,7 @@ class Timeout: ) def __attrs_post_init__(self): - if self.timeout <= 0.0: + if self.timeout < 0.0: raise ValueError("timeout must be positive") self._deadline = time.monotonic() + self.timeout From 22305366649e3bbf6b3e99586ae53df73062e9f8 Mon Sep 17 00:00:00 2001 From: Bastian Krause Date: Thu, 16 Jun 2022 00:42:57 +0200 Subject: [PATCH 2/2] remote/client: no block in async console, loop even on unavailable resource `labgrid-client console --loop` should keep trying to connect to the console even if the serial port is currently unvailable. The console method is declared async because it needs to handle crossbar communication with the coordinator concurrently, see [1] (e.g. place kicks, resouces becoming available). Until now it only tries again if microcom comes back with a return code != 0. But in case the resource is unavailable, labgrid bails out early because target.get_resource() raises a NoResourceFoundError. Calling target.get_resource() waits until the resources are available by default. This is done in a blocking poll()/time.sleep() loop until a timeout is reached allowing no crossbar communication in the meantime. This again means changes in resource availability we wait for cannot arrive. So there is no point in waiting for them at all. Fix that by awaiting the NetworkSerialPort resource while allowing other tasks to run when --loop is given. Afterwards "wait" for the NetworkSerialPort for 0 seconds. This either succeeds or a NoResourceFoundError is raised, both happens instantly. [1] 19757a18 ("remote/client: close console connection on place release") Signed-off-by: Bastian Krause --- labgrid/remote/client.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/labgrid/remote/client.py b/labgrid/remote/client.py index 1bac0c401..8a76cddfa 100755 --- a/labgrid/remote/client.py +++ b/labgrid/remote/client.py @@ -31,7 +31,7 @@ from .. import Target, target_factory from ..util.proxy import proxymanager from ..util.helper import processwrapper -from ..util import atomic_replace +from ..util import atomic_replace, Timeout from ..driver import Mode txaio.config.loop = asyncio.get_event_loop() @@ -838,10 +838,20 @@ def digital_io(self): elif action == 'low': drv.set(False) - async def _console(self, place, target, *, logfile=None): + async def _console(self, place, target, timeout, *, logfile=None, loop=False): name = self.args.name from ..resource import NetworkSerialPort - resource = target.get_resource(NetworkSerialPort, name=name) + resource = target.get_resource(NetworkSerialPort, name=name, wait_avail=False) + + # async await resources + timeout = Timeout(timeout) + while not resource.avail and (loop or not timeout.expired): + target.update_resources() + await asyncio.sleep(0.1) + + # use zero timeout to prevent blocking sleeps + target.await_resources([resource], timeout=0.0) + host, port = proxymanager.get_host_and_port(resource) # check for valid resources @@ -883,7 +893,8 @@ async def _console(self, place, target, *, logfile=None): async def console(self, place, target): while True: - res = await self._console(place, target, logfile=self.args.logfile) + res = await self._console(place, target, 10.0, logfile=self.args.logfile, + loop=self.args.loop) if res: break if not self.args.loop: