namespace ZB.MOM.WW.OtOpcUa.AdminUI.Browsing;
///
/// Closes a browse session that finished opening after its host went away (R04 L4). Every picker and
/// RawBrowseModal awaited and then stored the token;
/// if the host was disposed (or the modal closed) meanwhile, its teardown had already run with no token,
/// so the session — an OPC UA client session, an MQTT connection, a SQL connection against the plant —
/// stayed open until the reaper's idle TTL (2 min + one 30 s tick).
///
public static class BrowseOpenGuard
{
///
/// Call right after returns. When the host is gone it
/// closes the new session (fire-and-forget, like the hosts' own teardown) and returns
/// , telling the caller not to adopt the token.
///
/// The open result.
/// Whether the host was disposed (or closed) while the open was in flight.
/// The browse service the session was opened on.
/// when the caller must drop the result.
public static bool CloseIfOrphaned(BrowseOpenResult result, bool hostGone, IBrowserSessionService service)
{
if (!hostGone) return false;
if (result.Ok && result.Token != Guid.Empty)
{
// Never block (or throw into) a dead host's continuation on a slow remote close.
_ = CloseQuietlyAsync(service, result.Token);
}
return true;
}
private static async Task CloseQuietlyAsync(IBrowserSessionService service, Guid token)
{
try
{
await service.CloseAsync(token).ConfigureAwait(false);
}
catch
{
// The reaper still bounds a session whose close failed.
}
}
}