Multi-threading in Omniverse extensions

Hello,

I am writing an extension in omniverse code V 2022.2.0 and I have a use case where a lot of processing needs to take place in the background while keeping omniverse application responsive. I can successfully start a thread from my extension, but my problem is that I can’t use omniverse functions from within the thread.

Example: I want to start a background job when a button is pressed. This background task will create a new prim in my scene every 5 seconds.

def my_task():
    stage = omni.usd.get_context().get_stage()
    for i in range(5):
    time.sleep(1)
    stage.DefinePrim(f"/World/prim_{i}", "Xform")
 
 def start_my_task():
    from threading import Thread
    thread = Thread(target=my_task)
    thread.start()
 
with ui.HStack(height=10):
    ui.Button("Start", clicked_fn=start_my_task)

The error I get:

2022-12-08 08:24:34 [Error] [omni.kit.app.impl] [py stderr]: Exception in thread Thread-18:
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] Traceback (most recent call last):
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/threading.py”, line 926, in _bootstrap_inner
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] self.run()
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/threading.py”, line 870, in run
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] self._target(*self._args, **self.kwargs)
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/AIPE/AIPE/exts/techoffice.aipipeline/techoffice/aipipeline/ui/pose_estimation.py”, line 120, in my_task
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] stage.DefinePrim(f"/World/prim
{i}", “Xform”)
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/extscore/omni.usd.libs/pxr/Trace/init.py”, line 78, in invoke
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] return func(*args, **kwargs)
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/extscore/omni.kit.viewport.menubar.camera/omni/kit/viewport/menubar/camera/camera_menu_container.py”, line 157, in _on_usd_changed
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] self.__prim_changed_task = asyncio.ensure_future(self.__delayed_prim_changed())
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/asyncio/tasks.py”, line 607, in ensure_future
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] loop = events.get_event_loop()
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] File “/home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/asyncio/events.py”, line 644, in get_event_loop
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] % threading.current_thread().name)
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] RuntimeError: There is no current event loop in thread ‘Thread-18’.
2022-12-08 08:24:34 [Error] [omni.kit.app.impl]
2022-12-08 08:24:34 [Error] [omni.kit.app.impl] [py stderr]:
2022-12-08 08:24:34 [Error] [omni.kit.app.impl]
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] [py stderr]: /home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/selectors.py:72: RuntimeWarning: coroutine ‘StageModel.__delayed_prim_changed’ was never awaited
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] return self._selector._fd_to_key[fd]
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] RuntimeWarning: Enable tracemalloc to get the object allocation traceback
2022-12-08 08:25:25 [Error] [omni.kit.app.impl]
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] [py stderr]: /home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/selectors.py:72: RuntimeWarning: coroutine ‘PrimCaching._update_usd_cache_state’ was never awaited
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] return self._selector._fd_to_key[fd]
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] RuntimeWarning: Enable tracemalloc to get the object allocation traceback
2022-12-08 08:25:25 [Error] [omni.kit.app.impl]
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] [py stderr]: /home/anthony/.local/share/ov/pkg/deps/a2b9fc40eb4e1a4f50259f9f40e47fc3/python/lib/python3.7/selectors.py:72: RuntimeWarning: coroutine ‘_USDWatch.__delayed_prim_changed’ was never awaited
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] return self._selector._fd_to_key[fd]
2022-12-08 08:25:25 [Error] [omni.kit.app.impl] RuntimeWarning: Enable tracemalloc to get the object allocation traceback
2022-12-08 08:25:25 [Error] [omni.kit.app.impl]

Hi @anthony.yaghi. While threading is possible in OV, USD authoring as you are doing in your secondary thread with stage.DefinePrim() is not thread-safe: Universal Scene Description: Threading Model and Performance Considerations

We typically user coroutines instead of threads in OV to keep the UI responsive.

1 Like

Hi, thank you for your response. Could you share a code snippet showing how to use coroutines to asynchronously update the scene while keeping the UI responsive ? Any simple example will do.

Thank you

Hi @anthony.yaghi. Here’s an example to give you an idea on how to go about it:

import asyncio

import omni.kit.commands
import omni.ui as ui


my_window = ui.Window("Example Window", width=300, height=300)
with my_window.frame:
    with ui.VStack():

        async def clicked():
            for x in range(10):
                omni.kit.commands.execute('CreateMeshPrimWithDefaultXform',prim_type='Cube')
                await asyncio.sleep(1)

        ui.Button("Create Cubes", clicked_fn=lambda: asyncio.ensure_future(clicked()))