fix(relay): release session lock before callbacks

Signed-off-by: Alex Fournier <afournier@nvidia.com>
This commit is contained in:
Alex Fournier 2026-07-23 13:33:41 -07:00
parent e862099dff
commit 591ba267d2
2 changed files with 35 additions and 6 deletions

View file

@ -201,14 +201,15 @@ class RelayRuntime:
raise RuntimeError("Hermes Relay session is closing")
if session.context is None or session.handle is None:
raise RuntimeError("Hermes Relay session context is unavailable")
context = session.context.copy()
def invoke() -> Any:
self.relay.get_scope_stack()
return callback(*args, **kwargs)
def invoke() -> Any:
self.relay.get_scope_stack()
return callback(*args, **kwargs)
# A copy permits a helper called by an existing Relay callback to
# re-enter the same logical session without re-entering Context.
return session.context.copy().run(invoke)
# A copy permits a helper called by an existing Relay callback to
# re-enter the same logical session without re-entering Context.
return context.run(invoke)
async def run_in_session_async(
self,

View file

@ -1182,6 +1182,34 @@ def test_async_session_runner_awaits_inside_saved_relay_context(direct_runtime):
assert result == session.handle
def test_sync_session_runner_releases_lock_before_callback(direct_runtime):
runtime = relay_runtime.get_runtime()
assert runtime is not None
session = runtime.ensure_session({"session_id": "sync-session"})
assert session is not None
acquired = threading.Event()
contender = None
def probe() -> Any:
nonlocal contender
def acquire_session_lock() -> None:
with session.lock:
acquired.set()
contender = threading.Thread(target=acquire_session_lock)
contender.start()
assert acquired.wait(timeout=1)
return direct_runtime._scope.get()
result = runtime.run_in_session(session, probe)
assert contender is not None
contender.join(timeout=1)
assert result == session.handle
assert contender.is_alive() is False
def test_active_turn_requires_matching_session_and_profile(
direct_runtime,
tmp_path,