fix: a source that has sent no CONFIG reads connecting, not connected
A stream that answered CONNECT but has sent no CONFIG has no signal list, so nothing can be enabled and no DATA frame is owed. Calling that "connected, but no data is arriving: another reader may hold the source" blamed a second reader that does not exist, and the badge blinked green over it -- a working pipeline is exactly what it did not have. The glance word is `connecting` (ten characters, no wider than `event held`, so the fixed-set layout rule still holds) and the badge takes a fourth, steady yellow phase, both from the first frame rather than after the three-second stall test. The stall notice itself now splits on `have_config`: the unicast contention wording where a signal list exists, "trying to connect" where it does not, with `[udpscope] connecting:` on stderr beside the old `starved:`.
This commit is contained in:
1 parent
b313cac46e
commit
811c6d80f0
5 files changed
+145
-52
No files matched your search
+62
-21
@@ -452,7 +452,7 @@ struct App {
|
||||
/// Frames the badge spent in each `BadgePhase`. Both `lit` and `dim` have to be non-zero
|
||||
/// over a run that acquired anything -- that is what "it blinks" means, and a badge
|
||||
/// stuck lit looks identical to a working one in a screenshot.
|
||||
int dbg_badge_frames[3] = {0, 0, 0};
|
||||
int dbg_badge_frames[4] = {0, 0, 0, 0};
|
||||
int dbg_reset_visible = -1;
|
||||
std::vector<std::uint64_t> last_stored;
|
||||
std::uint64_t dbg_latches_at_disarm = 0;
|
||||
@@ -926,10 +926,14 @@ std::string format_window(double s) {
|
||||
|
||||
/// What the activity badge is saying. `Lit`/`Dim` are the two halves of the blink, so
|
||||
/// the badge is only *blinking* while the pipeline is actually running; a steady badge is
|
||||
/// a stopped one, which is the state worth noticing on a glance.
|
||||
enum class BadgePhase { Lit = 0, Dim = 1, Idle = 2 };
|
||||
/// a stopped one, which is the state worth noticing on a glance. `Connecting` is the
|
||||
/// half-way state -- the socket answered but the stream has sent no CONFIG, so there is no
|
||||
/// signal list and nothing to acquire -- and it is steady yellow rather than green,
|
||||
/// because a blinking green badge over a source that has sent nothing claims a working
|
||||
/// pipeline.
|
||||
enum class BadgePhase { Lit = 0, Dim = 1, Idle = 2, Connecting = 3 };
|
||||
|
||||
/// The badge is one glyph in one of three colours. It must stay the *same glyph*: the
|
||||
/// The badge is one glyph in one of four colours. It must stay the *same glyph*: the
|
||||
/// status row sits above the plot, and a badge that changed width as it blinked would
|
||||
/// move the label beside it and re-lay out the plot twice a second.
|
||||
const char* badge_markup(BadgePhase phase) {
|
||||
@@ -938,6 +942,8 @@ const char* badge_markup(BadgePhase phase) {
|
||||
return "<span foreground=\"#45E045\">\u25CF</span>";
|
||||
case BadgePhase::Dim:
|
||||
return "<span foreground=\"#1C5A1C\">\u25CF</span>";
|
||||
case BadgePhase::Connecting:
|
||||
return "<span foreground=\"#E0B020\">\u25CF</span>";
|
||||
case BadgePhase::Idle:
|
||||
break;
|
||||
}
|
||||
@@ -5440,7 +5446,12 @@ gboolean tick(gpointer user) {
|
||||
}
|
||||
StreamStats& st = model.stats();
|
||||
const char* mode = "live";
|
||||
if (trigger_on) {
|
||||
if (!st.have_config.load()) {
|
||||
// Ahead of every other branch for the reason the glance word is: with no CONFIG there is no
|
||||
// signal list, so "live" (or "armed", which cannot happen without one) would name a pipeline
|
||||
// that has not been described yet.
|
||||
mode = "trying to connect: no configuration, and so no signal list, from the stream yet";
|
||||
} else if (trigger_on) {
|
||||
if (app->capturing) {
|
||||
mode = "trigger: CAPTURING (post-trigger window filling)";
|
||||
} else if (app->event_held) {
|
||||
@@ -5465,14 +5476,26 @@ gboolean tick(gpointer user) {
|
||||
// green light over a dead socket is the one reading that must not happen. The blink is
|
||||
// a 1 Hz square wave off the monotonic clock rather than its own timer, so it cannot
|
||||
// drift away from the frame it is drawn on.
|
||||
// A source that answered CONNECT but has sent no CONFIG is *connecting*, not acquiring:
|
||||
// there is no signal list, so nothing can be enabled and no DATA frame can arrive. It gets
|
||||
// the steady yellow rather than the blinking green, because the green says "the pipeline is
|
||||
// running end to end" and here it has not started.
|
||||
const bool paused = app->acq->paused();
|
||||
const bool active = st.connected.load() && !paused && (!trigger_on || armed);
|
||||
const bool connecting = st.connected.load() && !st.have_config.load();
|
||||
const bool active =
|
||||
st.connected.load() && !connecting && !paused && (!trigger_on || armed);
|
||||
const bool lit = ((g_get_monotonic_time() / (G_TIME_SPAN_SECOND / 2)) & 1) == 0;
|
||||
const BadgePhase phase =
|
||||
!active ? BadgePhase::Idle : (lit ? BadgePhase::Lit : BadgePhase::Dim);
|
||||
const int reason = active ? 0 : !st.connected.load() ? 1 : paused ? 2 : 3;
|
||||
const BadgePhase phase = connecting ? BadgePhase::Connecting
|
||||
: !active ? BadgePhase::Idle
|
||||
: (lit ? BadgePhase::Lit : BadgePhase::Dim);
|
||||
const int reason = active ? 0
|
||||
: !st.connected.load() ? 1
|
||||
: connecting ? 4
|
||||
: paused ? 2
|
||||
: 3;
|
||||
static const char* const kWhy[] = {"acquiring", "waiting for the stream", "paused",
|
||||
"trigger disarmed"};
|
||||
"trigger disarmed",
|
||||
"trying to connect: no configuration from the stream yet"};
|
||||
// Keyed on the reason as well as the phase: three of the reasons share the Idle phase,
|
||||
// so a pause that becomes a disarm has to reach the tooltip.
|
||||
++app->dbg_badge_frames[static_cast<int>(phase)];
|
||||
@@ -5509,8 +5532,16 @@ gboolean tick(gpointer user) {
|
||||
|
||||
// Derive the one-word state for the glance line from the same conditions the mode prose
|
||||
// branches on, so the two cannot disagree.
|
||||
//
|
||||
// `connecting` is first and covers both halves of the handshake -- no socket yet, and a
|
||||
// socket with no CONFIG behind it -- because in neither is there a signal list, and every
|
||||
// other word here claims one. The badge is what distinguishes them (grey against yellow):
|
||||
// the word says what the app is doing, the colour says how far it got. It is no wider than
|
||||
// `event held`, which is what lets it join a set the plot's layout is measured against.
|
||||
const char* state_word = "live";
|
||||
if (display == DisplayMode::Capturing) {
|
||||
if (!st.have_config.load()) {
|
||||
state_word = "connecting";
|
||||
} else if (display == DisplayMode::Capturing) {
|
||||
state_word = "capturing";
|
||||
} else if (display == DisplayMode::Held) {
|
||||
state_word = "event held";
|
||||
@@ -5685,9 +5716,6 @@ void note_starvation(App& app) {
|
||||
const std::uint64_t reconnects = st.reconnects.load(std::memory_order_relaxed);
|
||||
const bool connected = st.connected.load(std::memory_order_relaxed);
|
||||
|
||||
// No `have_config` here: the losing client of a single-client source need never see a
|
||||
// CONFIG at all (the server answered the first one), and "connected, kicked, nothing
|
||||
// arriving" is the state worth naming either way.
|
||||
// "Connected, but nothing is arriving" is a *rate* fact, not a reconnect one: a source that
|
||||
// answers CONNECT and then goes quiet -- the unicast case where another client took the session --
|
||||
// never reconnects while it is silent, so requiring a reconnect made the notice invisible in
|
||||
@@ -5706,16 +5734,28 @@ void note_starvation(App& app) {
|
||||
// stream was arriving perfectly well.
|
||||
return;
|
||||
}
|
||||
// Two diagnoses, split on `have_config`, because they are not the same situation. A source
|
||||
// that sent a CONFIG and then went quiet is the unicast fight: the signal list is here, so
|
||||
// something took the stream away. One that has sent no CONFIG at all never had the stream to
|
||||
// lose -- there is no signal list, so nothing can be enabled and no DATA frame is owed -- and
|
||||
// naming that "connected, but no data is arriving" blamed a second reader that does not exist.
|
||||
// It is still the same stall test: a CONFIG that arrives promptly leaves no notice at all.
|
||||
const bool have_config = st.have_config.load(std::memory_order_relaxed);
|
||||
app.notice =
|
||||
"connected, but no data is arriving: another reader may hold the source (a unicast "
|
||||
"source serves one client at a time) -- use --multicast to watch from several UIs";
|
||||
have_config
|
||||
? "connected, but no data is arriving: another reader may hold the source (a unicast "
|
||||
"source serves one client at a time) -- use --multicast to watch from several UIs"
|
||||
: "trying to connect: the source answered, but has sent no configuration, so there is "
|
||||
"no signal list yet -- check that the streamer is running and serving this port";
|
||||
app.notice_until_us = g_get_monotonic_time() + 3 * G_TIME_SPAN_SECOND;
|
||||
if (!app.starve_reported) {
|
||||
app.starve_reported = true;
|
||||
std::fprintf(stderr,
|
||||
"[udpscope] starved: connected (config=%d), reconnects=%llu, no new frames "
|
||||
"in the last second -- a unicast source serves one client at a time\n",
|
||||
st.have_config.load(std::memory_order_relaxed) ? 1 : 0,
|
||||
have_config
|
||||
? "[udpscope] starved: connected (config=1), reconnects=%llu, no new frames "
|
||||
"in the last second -- a unicast source serves one client at a time\n"
|
||||
: "[udpscope] connecting: connected (config=0), reconnects=%llu, no "
|
||||
"configuration from the stream -- there is no signal list to acquire\n",
|
||||
static_cast<unsigned long long>(reconnects));
|
||||
std::fflush(stderr);
|
||||
}
|
||||
@@ -8342,8 +8382,9 @@ int run_ui(int argc, char** argv, const Config& cfg, ScopeModel& model, Acquisit
|
||||
: 0.0);
|
||||
}
|
||||
}
|
||||
std::printf("[udpscope] badge frames: lit=%d dim=%d idle=%d\n", app.dbg_badge_frames[0],
|
||||
app.dbg_badge_frames[1], app.dbg_badge_frames[2]);
|
||||
std::printf("[udpscope] badge frames: lit=%d dim=%d idle=%d connecting=%d\n",
|
||||
app.dbg_badge_frames[0], app.dbg_badge_frames[1], app.dbg_badge_frames[2],
|
||||
app.dbg_badge_frames[3]);
|
||||
std::printf("[udpscope] trigger panel revealed before the probe toggled it: %d; "
|
||||
"signal list items=%d selected=%d; column visible after sidebar "
|
||||
"toggle off=%d\n",
|
||||
|
||||
Reference in new issue
Block a user