Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -778,14 +778,14 @@ public async Task Overlapping_observations_of_one_peer_both_read_the_live_mappin
var file = PeerFile();
tool.BindPeerDeviceDescription(Peer, file);
using var entered = new SemaphoreSlim(0, 1);
using var release = new SemaphoreSlim(0, 1);
using var release = new ManualResetEventSlim();
var held = 0;
using var server = new ScriptedUploadServer(responder, Peer, (index, sub) =>
{
if (index == 0x1800 && sub == 1 && System.Threading.Interlocked.Exchange(ref held, 1) == 0)
{
entered.Release();
release.Wait(ShortTimeout);
release.Wait();
}
if (index is >= 0x1400 and <= 0x1403 or >= 0x1800 and <= 0x1803)
return index == 0x1800 && sub == 1 ? U32(Tpdo1) : null;
Expand All @@ -795,26 +795,33 @@ public async Task Overlapping_observations_of_one_peer_both_read_the_live_mappin

var firstSink = new ListSink();
var secondSink = new ListSink();
var first = tool.ObserveForeignPdoAsync(Peer, Tpdo1, new byte[] { 0x5A }, file, firstSink);
entered.Wait(ShortTimeout).Should().BeTrue();
// The call runs synchronously up to its first wait, which is the per-peer queue while the
// first observation holds it; without that queue it would take the peer's busy SDO
// channel right here, be refused, and complete from the file. So when it returns, the
// overlap under test has happened -- no sleep is needed to let it queue.
var second = tool.ObserveForeignPdoAsync(Peer, Tpdo1, new byte[] { 0xA5 }, file, secondSink);
second.IsCompleted.Should().BeFalse("the second observation waits for the first");
release.Release();

var firstResult = await first.WithTimeoutAsync(ShortTimeout);
var secondResult = await second.WithTimeoutAsync(ShortTimeout);
firstResult.Observations.Should().ContainSingle();
firstResult.Observations[0].Origin.Should().Be(ForeignPdoMappingOrigin.LiveMapping);
firstSink.Signals.Should().ContainSingle();
firstSink.Signals[0].Value.Should().Equal(0x5A);
secondResult.Observations.Should().ContainSingle();
secondResult.Observations[0].Origin.Should().Be(ForeignPdoMappingOrigin.LiveMapping);
secondSink.Signals.Should().ContainSingle();
secondSink.Signals[0].Value.Should().Equal(0xA5);
try
{
var first = tool.ObserveForeignPdoAsync(Peer, Tpdo1, new byte[] { 0x5A }, file, firstSink);
entered.Wait(ShortTimeout).Should().BeTrue();
// The call runs synchronously up to its first wait, which is the per-peer queue while the
// first observation holds it; without that queue it would take the peer's busy SDO
// channel right here, be refused, and complete from the file. So when it returns, the
// overlap under test has happened -- no sleep is needed to let it queue.
var second = tool.ObserveForeignPdoAsync(Peer, Tpdo1, new byte[] { 0xA5 }, file, secondSink);
second.IsCompleted.Should().BeFalse("the second observation waits for the first");
release.Set();

var firstResult = await first.WithTimeoutAsync(ShortTimeout);
var secondResult = await second.WithTimeoutAsync(ShortTimeout);
firstResult.Observations.Should().ContainSingle();
firstResult.Observations[0].Origin.Should().Be(ForeignPdoMappingOrigin.LiveMapping);
firstSink.Signals.Should().ContainSingle();
firstSink.Signals[0].Value.Should().Equal(0x5A);
secondResult.Observations.Should().ContainSingle();
secondResult.Observations[0].Origin.Should().Be(ForeignPdoMappingOrigin.LiveMapping);
secondSink.Signals.Should().ContainSingle();
secondSink.Signals[0].Value.Should().Equal(0xA5);
}
finally
{
release.Set();
}
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2657,44 +2657,52 @@ private static async Task CallerCancelsASendQueuedOnTheGateAsync(bool disposeAsL
using var bus = OpenClassic(session, 0);
using var service = new CanBusService(bus);
using var actor = new ProtocolActor();
var channel = new IsoTpChannel(service, IsoTpEndpoint.Normal(0x123, 0x321), FastOptions(),
ownsService: false, actor);
using var release = new ManualResetEventSlim();
using var callerCts = new CancellationTokenSource();
var busy = actor.PostAsync(() => release.Wait(TimeSpan.FromSeconds(30)));

var send1 = channel.SendAsync(new byte[] { 0x01 });
var send2 = channel.SendAsync(new byte[] { 0x02 }, callerCts.Token);
send2.IsCompleted.Should().BeFalse();
// Ends once Dispose is under way, and so after its first line, which marks the channel
// disposed: from then on the actor fails send1 instead of beginning it.
var receive = channel.ReceiveAsync();
var late = new LateDisposal(disposeAsLateAsPossible, send1);

try
using (var channel = new IsoTpChannel(service, IsoTpEndpoint.Normal(0x123, 0x321), FastOptions(),
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
ownsService: false, actor))
{
callerCts.Cancel();
var queued = await Assert.ThrowsAsync<OperationCanceledException>(() => send2.WaitAsync(ShortTimeout));
queued.CancellationToken.Should().Be(callerCts.Token);
}
finally
{
var disposal = Task.Run(async () =>
using var release = new ManualResetEventSlim();
using var callerCts = new CancellationTokenSource();
var busy = actor.PostAsync(() => release.Wait());

try
{
await late.MayStartAsync().ConfigureAwait(false);
channel.Dispose();
});
// Released only once the disposal is seen to have begun (#400): released earlier,
// send1 can begin and complete before Dispose reaches it, and then it owes no
// ObjectDisposedException.
await late.WaitForAsync(receive);
late.ActorReleased();
release.Set();
await busy;
await disposal.WaitAsync(ShortTimeout);
var send1 = channel.SendAsync(new byte[] { 0x01 });
var send2 = channel.SendAsync(new byte[] { 0x02 }, callerCts.Token);
send2.IsCompleted.Should().BeFalse();
// Ends once Dispose is under way, and so after its first line, which marks the channel
// disposed: from then on the actor fails send1 instead of beginning it.
var receive = channel.ReceiveAsync();
var late = new LateDisposal(disposeAsLateAsPossible, send1);

callerCts.Cancel();
var queued = await Assert.ThrowsAsync<OperationCanceledException>(() => send2.WaitAsync(ShortTimeout));
queued.CancellationToken.Should().Be(callerCts.Token);

// Observe Dispose as part of the test sequence. The using block and outer finally
// own failure cleanup, releasing the actor before the channel is torn down.
var disposal = Task.Run(async () =>
{
await late.MayStartAsync().ConfigureAwait(false);
channel.Dispose();
});
// Released only once the disposal is seen to have begun (#400): released earlier,
// send1 can begin and complete before Dispose reaches it, and then it owes no
// ObjectDisposedException.
await late.WaitForAsync(receive);
late.ActorReleased();
release.Set();
await busy;
await disposal.WaitAsync(ShortTimeout);

await Assert.ThrowsAsync<ObjectDisposedException>(() => send1.WaitAsync(ShortTimeout));
}
finally
{
// Also releases the actor when setup or an assertion fails before the disposal probe.
release.Set();
await busy.WaitAsync(ShortTimeout);
}
}

await Assert.ThrowsAsync<ObjectDisposedException>(() => send1.WaitAsync(ShortTimeout));
}

/// <summary>Starts a disposal as late as the test lets it: not before the test waits on it,
Expand Down
Loading