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
31 changes: 30 additions & 1 deletion MainWindow.IoTesting.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public partial class MainWindow
private IoTestSessionController? _activeIoTestSessionController;
private long _ioTestObservationSequence;
private int? _pollingIntervalBeforeIoFat;
private int _ioFatEvidenceDrainDispatchActive;

protected override void OnInitialized(EventArgs e)
{
Expand Down Expand Up @@ -401,9 +402,37 @@ private IoTestSessionController CreateIoTestSession(IoTestProject project, strin
=> new(
project,
ResolveIoTestDevice,
action => Dispatcher.BeginInvoke(action, DispatcherPriority.Background),
DispatchIoFatEvidence,
evidenceRoot);

private void DispatchIoFatEvidence(Action action)
{
ArgumentNullException.ThrowIfNull(action);

// A newly observed edge must reach the FAT evidence evaluator before normal UI
// background work, preserving the immediate TRUE-edge behavior from #150. When
// the bounded drain reschedules itself from inside the current callback, lower the
// continuation to Background so Render/Input are not starved by a large snapshot burst.
var priority = Volatile.Read(ref _ioFatEvidenceDrainDispatchActive) == 0
? DispatcherPriority.DataBind
: DispatcherPriority.Background;

Dispatcher.BeginInvoke(
new Action(() =>
{
Interlocked.Increment(ref _ioFatEvidenceDrainDispatchActive);
try
{
action();
}
finally
{
Interlocked.Decrement(ref _ioFatEvidenceDrainDispatchActive);
}
}),
priority);
}

private static string IoTestingProjectsRoot() => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"ARSAS",
Expand Down
12 changes: 10 additions & 2 deletions tests/ARSAS.Tests/IoTestFatP0RegressionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public void FatConnect_ReadinessDoesNotBlockOnLegacyEightToEighteenSecondReportS
}

[Fact]
public void FatEvidenceDrain_YieldsUiAndStartupJournalUsesOneDurableBatch()
public void FatEvidenceDrain_PrioritizesFirstEdgeButYieldsContinuationAndBatchesStartupJournal()
{
var controller = Read("Services/IoTesting/IoTestSessionController.cs");
var journal = Read("Services/IoTesting/IoTestEvidenceJournal.cs");
Expand All @@ -41,8 +41,16 @@ public void FatEvidenceDrain_YieldsUiAndStartupJournalUsesOneDurableBatch()
Assert.Contains("AppendBatchRequired(startupEntries)", controller, StringComparison.Ordinal);
Assert.Contains("AppendBatch(IEnumerable<IoTestJournalEntry> entries)", journal, StringComparison.Ordinal);
Assert.Contains("FlushDurable();", journal, StringComparison.Ordinal);

// Preserve #150's immediate TRUE-edge scheduling while retaining P0's bounded
// drain/yield behavior for bursts. The first dispatch is DataBind; a reschedule
// requested while that drain is active is intentionally demoted to Background.
Assert.Contains("DispatchIoFatEvidence", main, StringComparison.Ordinal);
Assert.Contains("Volatile.Read(ref _ioFatEvidenceDrainDispatchActive) == 0", main, StringComparison.Ordinal);
Assert.Contains("DispatcherPriority.DataBind", main, StringComparison.Ordinal);
Assert.Contains("DispatcherPriority.Background", main, StringComparison.Ordinal);
Assert.DoesNotContain("DispatcherPriority.DataBind", main, StringComparison.Ordinal);
Assert.Contains("Interlocked.Increment(ref _ioFatEvidenceDrainDispatchActive)", main, StringComparison.Ordinal);
Assert.Contains("Interlocked.Decrement(ref _ioFatEvidenceDrainDispatchActive)", main, StringComparison.Ordinal);
}

[Fact]
Expand Down
Loading