mirror of
https://github.com/OPCFoundation/UA-.NETStandard.git
synced 2026-10-21 17:16:16 +08:00
SessionBindingTests: let readers confirm the live session before racing transfers (#4848)
## Problem `BindingLivenessRacingTransfersAndCloseNeverConfirmsReplacedBindingAsync` failed on the net48 CI runner (test-windows-net48 11/16, run 39250649914, on docs-only PR #4841) with `confirmed = 0`. The safety check (`errors`) was empty. The reader tasks were not scheduled until all 1000 transfers and the close had finished, so the liveness check never saw a live binding. ## Fix (test only) Before the first transfer, the test now waits until every reader has confirmed the live binding at least once (bounded: 30 s, then a clear `Assert.Fail`). The race still covers transfers and close, but the positive assertion no longer depends on thread-pool scheduling. The safety assertion is unchanged. ## Verification I ran the test with a temporary `[Repeat(100)]` in Release builds: - net10.0 and net48: 100 repeats each, no load - net10.0 and net48: 3 × 100 repeats each with 2 busy loops per core 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Time.Testing;
|
||||
@@ -619,6 +620,7 @@ namespace Opc.Ua.Server.Tests
|
||||
int closed = 0;
|
||||
int checks = 0;
|
||||
int confirmed = 0;
|
||||
int readersLive = 0;
|
||||
var errors = new ConcurrentQueue<string>();
|
||||
using var stop = new CancellationTokenSource();
|
||||
|
||||
@@ -629,6 +631,7 @@ namespace Opc.Ua.Server.Tests
|
||||
readers[r] = Task.Run(() =>
|
||||
{
|
||||
int attempt = 0;
|
||||
bool seenLive = false;
|
||||
while (!stop.IsCancellationRequested)
|
||||
{
|
||||
OperationContext channel = channels[(reader + attempt++) & 1];
|
||||
@@ -650,6 +653,11 @@ namespace Opc.Ua.Server.Tests
|
||||
break;
|
||||
}
|
||||
Interlocked.Increment(ref confirmed);
|
||||
if (!seenLive)
|
||||
{
|
||||
seenLive = true;
|
||||
Interlocked.Increment(ref readersLive);
|
||||
}
|
||||
if (closedBefore || binding.ActivationSequence < floor)
|
||||
{
|
||||
errors.Enqueue(
|
||||
@@ -662,6 +670,18 @@ namespace Opc.Ua.Server.Tests
|
||||
|
||||
try
|
||||
{
|
||||
// let every reader confirm the live session once before the race starts, so the
|
||||
// positive check does not depend on the pool scheduling readers during transfers
|
||||
var deadline = Stopwatch.StartNew();
|
||||
while (Volatile.Read(ref readersLive) < kReaders && errors.IsEmpty)
|
||||
{
|
||||
if (deadline.Elapsed > TimeSpan.FromSeconds(30))
|
||||
{
|
||||
Assert.Fail(
|
||||
$"Only {Volatile.Read(ref readersLive)} of {kReaders} readers confirmed the live session.");
|
||||
}
|
||||
await Task.Delay(1).ConfigureAwait(false);
|
||||
}
|
||||
for (int i = 1; i <= kTransfers; i++)
|
||||
{
|
||||
await ActivateAsync(created, channels[i & 1]).ConfigureAwait(false);
|
||||
|
||||
Reference in New Issue
Block a user