Skip to content

Commit bb17e09

Browse files
committed
Use async omni.client APIs in assets utilities
The synchronous omni.client entry points (stat, copy, read_file) park Kit's main thread for the entire I/O round-trip and trigger Kit's "Detected a blocking function. Please switch to the async version" warning during config init when Nucleus URLs are reached. Switch the four call sites in assets.py (check_file_path, retrieve_file_path stat + copy, read_file) to their _async counterparts and drive them through a shared _drive_kit_async helper that schedules the coroutine on Kit's asyncio loop and ticks app.update() until it resolves. Add omni.kit.async_engine as a kit dependency so the loop is pumped each tick. Falls back to a private event loop when Kit is not running (unit tests).
1 parent 24f2cc0 commit bb17e09

2 files changed

Lines changed: 44 additions & 4 deletions

File tree

apps/isaaclab.python.kit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ keywords = ["experience", "app", "usd"]
5959
"omni.hydra.engine.stats" = {}
6060
"omni.hydra.rtx" = {}
6161
"omni.kit.mainwindow" = {}
62+
"omni.kit.async_engine" = {}
6263
"omni.kit.manipulator.camera" = {}
6364
"omni.kit.manipulator.prim" = {}
6465
"omni.kit.manipulator.selection" = {}

source/isaaclab/isaaclab/utils/assets.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,40 @@ def _parse_kit_asset_root() -> str:
5252
"""Path to the ``Isaac/IsaacLab`` directory on the NVIDIA Nucleus Server."""
5353

5454

55+
def _kit_app():
56+
"""Return the running Kit app or ``None`` if Kit is not running."""
57+
try:
58+
import omni.kit.app # noqa: PLC0415
59+
60+
return omni.kit.app.get_app()
61+
except Exception:
62+
return None
63+
64+
65+
def _drive_kit_async(coro):
66+
"""Run an ``omni.client`` ``_async`` coroutine without blocking Kit's main thread.
67+
68+
Schedules the coroutine on Kit's asyncio loop and ticks ``app.update()`` until
69+
it resolves; falls back to a private event loop when Kit isn't running.
70+
"""
71+
import asyncio # noqa: PLC0415
72+
import time # noqa: PLC0415
73+
74+
app = _kit_app()
75+
if app is None:
76+
return asyncio.get_event_loop().run_until_complete(coro)
77+
try:
78+
import omni.kit.async_engine # noqa: PLC0415
79+
80+
task = omni.kit.async_engine.run_coroutine(coro)
81+
except Exception:
82+
task = asyncio.ensure_future(coro)
83+
while not task.done():
84+
app.update()
85+
time.sleep(0)
86+
return task.result()
87+
88+
5589
def check_file_path(path: str) -> Literal[0, 1, 2]:
5690
"""Checks if a file exists on the Nucleus Server or locally.
5791
@@ -70,7 +104,7 @@ def check_file_path(path: str) -> Literal[0, 1, 2]:
70104

71105
import omni.client # noqa: PLC0415
72106

73-
if omni.client.stat(path.replace(os.sep, "/"))[0] == omni.client.Result.OK:
107+
if _drive_kit_async(omni.client.stat_async(path.replace(os.sep, "/")))[0] == omni.client.Result.OK:
74108
return 2
75109
else:
76110
return 0
@@ -131,7 +165,10 @@ def retrieve_file_path(path: str, download_dir: str | None = None, force_downloa
131165
if _UDIM_RE.search(cur_url):
132166
for tile in range(1001, 1101):
133167
tile_url = _UDIM_RE.sub(str(tile), cur_url)
134-
if omni.client.stat(tile_url.replace(os.sep, "/"))[0] == omni.client.Result.OK:
168+
if (
169+
_drive_kit_async(omni.client.stat_async(tile_url.replace(os.sep, "/")))[0]
170+
== omni.client.Result.OK
171+
):
135172
if tile_url not in visited:
136173
to_visit.append(tile_url)
137174
else:
@@ -143,7 +180,9 @@ def retrieve_file_path(path: str, download_dir: str | None = None, force_downloa
143180
os.makedirs(os.path.dirname(target_path), exist_ok=True)
144181

145182
if not os.path.isfile(target_path) or force_download:
146-
result = omni.client.copy(cur_url, target_path, omni.client.CopyBehavior.OVERWRITE)
183+
result = _drive_kit_async(
184+
omni.client.copy_async(cur_url, target_path, omni.client.CopyBehavior.OVERWRITE)
185+
)
147186
if result != omni.client.Result.OK and force_download:
148187
raise RuntimeError(f"Unable to copy file: '{cur_url}'. Is the Nucleus Server running?")
149188

@@ -183,7 +222,7 @@ def read_file(path: str) -> io.BytesIO:
183222
elif file_status == 2:
184223
import omni.client # noqa: PLC0415
185224

186-
file_content = omni.client.read_file(path.replace(os.sep, "/"))[2]
225+
file_content = _drive_kit_async(omni.client.read_file_async(path.replace(os.sep, "/")))[2]
187226
return io.BytesIO(memoryview(file_content).tobytes())
188227
else:
189228
raise FileNotFoundError(f"Unable to find the file: {path}")

0 commit comments

Comments
 (0)