using Opc.Ua;
namespace ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests;
///
/// Stalls condition-event delivery at one of the node manager's notifiers (Gitea #855): every condition event whose walk
/// reaches that notifier suspends there until , without blocking the reporter or holding the node
/// manager's Lock — so the condition's later events are deferred, as they were behind a full SDK channel before
/// folder items were delivered per item.
///
///
/// The pre-#855 tests produced deferral by holding a folder item's server-side MonitoredItem lock, which parked
/// the SDK's per-node delivery task. Folder items are now queued synchronously by the reporter (as Server-object items
/// always were), so holding an item lock would block the reporter instead; this goes through the node manager's test
/// probe (). The probe is consulted only at a notifier that has
/// event items (the hop the per-item delivery serves), so the stalled notifier must carry at least one.
///
internal sealed class NotifierStall : IDisposable
{
private readonly OtOpcUaNodeManager _nm;
private readonly TaskCompletionSource _released = new(TaskCreationOptions.RunContinuationsAsynchronously);
private readonly Func _probe;
private NotifierStall(OtOpcUaNodeManager nm, string notifierId)
{
_nm = nm;
_probe = node => !_released.Task.IsCompleted && node.NodeId.IdentifierAsString == notifierId ? _released.Task : null;
nm.NotifierDeliveryStall = _probe;
}
/// Starts stalling delivery at the notifier whose NodeId identifier is .
public static NotifierStall Hold(OtOpcUaNodeManager nm, string notifierId) => new(nm, notifierId);
/// Lets every stalled walk continue (on pool threads, off Lock).
public void Release() => _released.TrySetResult();
public void Dispose()
{
Release();
if (ReferenceEquals(_nm.NotifierDeliveryStall, _probe)) _nm.NotifierDeliveryStall = null;
}
}