/* logic/retry_ctl.h — 离线自动重试节奏闸(M10:断网重试风暴的修复点)。 * * 背景(真机 19000100_000000_7534.log + launcher meow_20261105_122541.log): * M9 的 30s 守卫写成 `if (!thr_on && (!last || now - last >= 30000))`,节奏完全依赖 * meow_ui_time_ms() 的 64 位差值。真机实测该值「低 32 位 = 整秒×1000(无毫秒)、高 32 位是垃圾」 * (见 REPORT §42),于是 now-last 几乎恒为巨大正数 → 每个 UI tick 都判「该重试」, * 断网时把「30s 一次」退化成「每个 tick 一次」的重试风暴。 * * 本模块把节奏判定收敛成三条硬约束(纯逻辑、零依赖,宿主编译可直接驱动): * 1) 时钟由调用方供给**可信单调钟**(home.c 传 core 的 ncm_monotonic_ms(),不再用 SDK 的 * meow_ui_time_ms());本模块只做差值,不取时间。 * 2) 时钟回退(now < last)绝不下溢成「巨大已超时」:按「刚试过」处理并重定基准, * 下一次尝试仍需重新等满 interval。 * 3) 单飞:busy(上一个 worker 未收工)期间任何时钟跳变都不发新尝试。 * 在线/离线抖动不重置基准 —— 抖动无法加速重试(旧实现 else 分支清零 g_offline_retry_ms, * 状态一抖就立刻再试,是风暴的第二条触发链)。 */ #ifndef NCM_RETRY_CTL_H #define NCM_RETRY_CTL_H #include typedef struct { int64_t last_ms; /* 上次发起尝试的单调毫秒时刻(have_last=0 时无意义) */ int have_last; /* 1=至少尝试过一次 */ } ncm_retry_ctl; void ncm_retry_ctl_init(ncm_retry_ctl *c); /* 每个 UI tick 调一次。返回 1=现在应当发起一次重试(内部已记账:调用方随即起 worker)。 * offline=当前是否离线;busy=是否已有 worker 在跑(单飞);now_ms=单调毫秒; * interval_ms=两次尝试的最小间隔(M10 定 30000)。 * * 语义: * - offline=0:返回 0,且**不动**基准(在线期间与状态抖动都不重置节奏)。 * - busy=1:返回 0。 * - 首次观察到离线:返回 1(进离线先立即探测一次,恢复后由会话门切回在线)。 * - 之后:仅当 now-last >= interval 才返回 1;now