mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-26 04:19:53 +08:00
Two related correctness fixes for the channel-reconnect path. 1. ChannelEntry.RequestReconnectAsync coalescing budget When multiple callers concurrently invoke RequestReconnectAsync, the first becomes the "starter" and its budget is passed to the cycle. Subsequent "joiners" were silently dropping their budget parameter — so a joiner with a strict 10-second deadline could not tighten an in-flight cycle that the starter began without one. Fix: add an m_effectiveBudget field guarded by m_lock. Every caller (starter + joiners) updates it via TighterOf(current, candidate), which returns the budget with smaller remaining time (null = "no constraint", so a null joiner never relaxes). The cycle re-reads GetEffectiveBudget() at the top of every iteration AND immediately after the inter-attempt delay, so a late joiner can preempt an in-flight sleep. The finally block clears m_effectiveBudget so the next cycle starts unconstrained. XML doc on RequestReconnectAsync(CancellationToken) describes the coalescing semantics in plain language. 2. ChannelReconnectPolicyBudget.ClampDelayToBudget exhaustion semantics For a custom IChannelReconnectPolicy that does NOT also implement IBudgetAwareChannelReconnectPolicy, when the shared retry budget was exhausted the helper returned the ORIGINAL delay unchanged instead of signaling "stop retrying". Downstream ChannelEntry.RunReconnectCycleAsync independently checks budget.IsExhausted so no extra reconnect attempt was issued, but the bug wasted one delay cycle (up to MaxDelay = 30 s default) before the fault transition was detected. Fix: return Timeout.InfiniteTimeSpan when TryConsume returns false — matching ExponentialBackoffChannelReconnectPolicy.GetDelay.