Skip to content

Commit

Permalink
USB/IP: Run Blocking Operations in Thread
Browse files Browse the repository at this point in the history
Some sysfs file writes block for more than 100 ms, which causes an
asyncio warning. Run those blocking writes in threads, so the event loop
isn't blocked.
  • Loading branch information
holesch committed Sep 10, 2024
1 parent b9a127a commit fee572b
Show file tree
Hide file tree
Showing 3 changed files with 23 additions and 9 deletions.
23 changes: 16 additions & 7 deletions not_my_board/_usbip.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def __init__(self, devices):

async def _context_stack(self, stack):
for _, device in self._devices.items():
stack.callback(device.restore_default_usb_driver)
stack.push_async_callback(device.restore_default_usb_driver)
await stack.enter_async_context(util.background_task(_refresh_task(device)))

async def handle_client(self, reader, writer):
Expand Down Expand Up @@ -143,12 +143,15 @@ def refresh(self):
async def _context_stack(self, stack):
await stack.enter_async_context(self._lock)
await self.available()
stack.callback(self.stop_export)
stack.push_async_callback(self.stop_export)

def stop_export(self):
async def stop_export(self):
if self._is_exported:
try:
(self._sysfs_path / "usbip_sockfd").write_text("-1\n")
# This can block for ~ 250 ms. Run it in a thread.
await util.run_in_thread(
(self._sysfs_path / "usbip_sockfd").write_text, "-1\n"
)
except (OSError, FileNotFoundError):
# client might have disconnected or device disappeared
pass
Expand Down Expand Up @@ -192,7 +195,10 @@ async def _ensure_usbip_host_driver(self):
logger.info(
'Unbinding USB device %s from driver "%s"', self._busid, driver_name
)
(driver_path / "unbind").write_text(self._busid)
# Unbinding can take more than 100 ms. Run in Thread.
await util.run_in_thread(
(driver_path / "unbind").write_text, self._busid
)
await self._bind_usbip_host_driver()
elif self._sysfs_path.exists():
await self._bind_usbip_host_driver()
Expand All @@ -205,15 +211,18 @@ async def _bind_usbip_host_driver(self):
(usbip_host_driver / "match_busid").write_text(f"add {self._busid}")
(usbip_host_driver / "bind").write_text(self._busid)

def restore_default_usb_driver(self):
async def restore_default_usb_driver(self):
driver_path = self._sysfs_path / "driver"
if driver_path.exists():
driver_name = driver_path.resolve().name
if driver_name == "usbip-host":
logger.info(
'Unbinding USB device %s from driver "%s"', self._busid, driver_name
)
(driver_path / "unbind").write_text(self._busid)
# Unbinding can take more than 100 ms. Run in Thread.
await util.run_in_thread(
(driver_path / "unbind").write_text, self._busid
)
self._bind_default_usb_driver()
elif self._sysfs_path.exists():
self._bind_default_usb_driver()
Expand Down
1 change: 1 addition & 0 deletions not_my_board/_util/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
relay_streams,
run,
run_concurrently,
run_in_thread,
)
from ._logging import configure_logging
from ._matching import find_matching
Expand Down
8 changes: 6 additions & 2 deletions not_my_board/_util/_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,9 +247,13 @@ async def __aexit__(self, exc_type, exc, tb):
async def flock(f):
"""File lock as a context manager"""

loop = asyncio.get_running_loop()
try:
await loop.run_in_executor(None, fcntl.flock, f.fileno(), fcntl.LOCK_EX)
await run_in_thread(fcntl.flock, f.fileno(), fcntl.LOCK_EX)
yield
finally:
fcntl.flock(f, fcntl.LOCK_UN)


async def run_in_thread(func, *args):
loop = asyncio.get_running_loop()
return await loop.run_in_executor(None, func, *args)

0 comments on commit fee572b

Please sign in to comment.