Do not trust the engine's count as the pace
The second probe run read TxBufLen as 0 one millisecond and again fifty milliseconds after twenty-one characters had been pushed, while those characters were already going out: the first of them was decoded back off the air 420 ms later. The number is right when it is a second old and wrong when it is fresh, so it runs behind the engine. A pump that fed on it would have handed over the whole message in half a second and put all of it beyond reach, which is worse than the clock it replaced. So the clock is the pace again, and the count is a check on it: the engine is never given more than Lead + Slack characters however fast the clock says to feed, and a message ends when the count and the clock estimate both say it has. Characters fed since the last answer are added to it, so a stale answer cannot be spent twice. The engine is set to Character out: ABCD with no space after it went out at once. Word out would have held it, so the holding case stays, but it now needs the count to be neither going down nor being added to, since an engine kept exactly at the cap has a count that does not move either. The probe writes down every reading for two seconds after a push and does not believe an empty one in that window, which is what the run needed to measure the lag and did not do. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RtspmWmS7f8kUvcyaHpRWZ
This commit is contained in:
@@ -41,6 +41,11 @@ public sealed class EngineProbe
|
||||
/// A count that has not moved for this long is not going to move.
|
||||
private static readonly TimeSpan Still = TimeSpan.FromSeconds(5);
|
||||
|
||||
/// How long after a push the count is written down every time it is read,
|
||||
/// and an empty answer is not believed. The count runs behind the engine,
|
||||
/// and this is where the report says by how much.
|
||||
private static readonly TimeSpan Settling = TimeSpan.FromSeconds(2);
|
||||
|
||||
/// How long the engine is given to key up and to drop again.
|
||||
private static readonly TimeSpan Keying = TimeSpan.FromSeconds(3);
|
||||
|
||||
@@ -126,6 +131,7 @@ public sealed class EngineProbe
|
||||
await KeyAsync(cancellation).ConfigureAwait(false);
|
||||
await TypeAsync(Message, cancellation).ConfigureAwait(false);
|
||||
log.Write($"pushed {Message.Length} characters");
|
||||
log.Write("every reading for the next two seconds: the first one that is not 0 says how far behind the count is");
|
||||
await DrainAsync(cancellation).ConfigureAwait(false);
|
||||
log.Write($"received while transmitting: \"{TakeReceived()}\"");
|
||||
await UnkeyAsync(cancellation).ConfigureAwait(false);
|
||||
@@ -173,20 +179,22 @@ public sealed class EngineProbe
|
||||
/// registered, not that the message has gone.
|
||||
private async Task DrainAsync(CancellationToken cancellation)
|
||||
{
|
||||
DateTime giveUp = DateTime.UtcNow + Patience;
|
||||
DateTime moved = DateTime.UtcNow;
|
||||
DateTime started = DateTime.UtcNow;
|
||||
DateTime giveUp = started + Patience;
|
||||
DateTime moved = started;
|
||||
int last = int.MinValue;
|
||||
bool wasEmpty = false;
|
||||
while (DateTime.UtcNow < giveUp)
|
||||
{
|
||||
int left = await AskAsync("", cancellation).ConfigureAwait(false);
|
||||
if (left != last)
|
||||
bool settling = DateTime.UtcNow - started < Settling;
|
||||
if (left != last || settling)
|
||||
{
|
||||
log.Write($"TxBufLen: {Answer(left)}");
|
||||
moved = left != last ? DateTime.UtcNow : moved;
|
||||
last = left;
|
||||
moved = DateTime.UtcNow;
|
||||
}
|
||||
if (left == 0 && wasEmpty)
|
||||
if (left == 0 && wasEmpty && !settling)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user