Files
MeowOS-SDK/sdk/meow_cachegov.c
T
AskaEth 27a3750e36 SDK: 同步主仓 M15/M16/bug3/MD3 修复 — 时钟 ABI 补全/IME 冷却窗/列表行高派生/形状 token/翻页键
公开 SDK 落后主仓的一批 bugfix(主仓是固件构建源,这些修复已在设备上跑过)。
本次只做「主仓 → 公开仓」单向同步,图像栈方向由主仓侧另一单处理。

时钟 ABI(M16 第二弹,原文件自认「残余未修」):
- meow_ui_time.c 147→345 行:futex 相对超时接管(__timedwait/_cp)、
  timerfd_create/settime/gettime 的 itimerspec 64↔32 双向转换、
  __clock_settime64 越界返 ENOTSUP,另有 4 个纯换算函数
- meow_ui_internal.h:struct meow_time32 / meow_itimerspec32 契约移入内部头
- CLOCK_ABI.md:补接管机制、验证配方与 objdump 证据
- 新增 test_meow_time64.c

IME bug3(成对才有意义):
- meow_ui_ime.c:IME_TAP_COOLDOWN_MS 200 冷却窗 + 打开时二次 drain
- meow_ui_input.c:meow_input_touch_reset() 并在 drain 内调用(复位触摸状态机)
- test_ime_fix.c:新增用例④ stray-tap

列表行高(M15,修用户可见 bug):
- meow_ui_widget.c:listv_item_h() 由主题字体行高派生,不再写死 56
  (两行列表 62 > 56 会让首行标题被裁剪守卫整条跳过)
- include/meow_ui_widgets.h:新增 meow_listview_item_h()
- 新增 test_meow_ui_listview_rowh.c

其余:meow_select.c / meow_ui_dialog.c 统一 MD3 形状 token;
include/meow_ipc.h 补 MSG_KEY_PAGEUP/PAGEDOWN 枚举;
meow_ui_sys.c / meow_cachegov.c 取时统一走 meow_ui_time_ms()。

测试辅助函数标定:tuku/test/test_settings_lock_host.c 的注入前等待 150ms→350ms。
该等待按修复前 IME 标定,新冷却窗下首键 dt_open≈114ms 会被当迟到抬起吞掉,
只录到 "68" 两位。仅改等待时长,断言原样保留。

验证:tuku 四套脚本全绿(run_tests 644 PASS / zip 106 / cbz 296 / webtoon PASS);
12 个源文件与主仓逐字节一致;sdk/build_sdk.sh 34 成员;
tuku/build_tuku.sh 产出静态 MIPS32r2 ELF。
2026-11-06 21:31:54 +08:00

301 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* sdk/meow_cachegov.c — 磁盘缓存 page-cache 治理 SDK 库(A:DONTNEED 主导侧)。
*
* 权威方案:devdocs/plan/OOM_PAGECACHE_GOVERNANCE.md。归属(ARCHITECTURE_PRINCIPLES ③混合):
* A 热路径(register/tick/drop_finished/refault)随 SDK 库;C(/proc/meminfo Cached 监控)
* 归 meow_svc_perf,本库只读其共享状态文件 MEOW_CACHEGOV_C_FILE 作为降载信号。
*
* 红线(务必保留本注释):**L2 禁止**——绝不丢 `[read_pos, ...)` 任何未解码区(含 read_pos 前方
* 刚写入尚未解码的页)。本库所有 drop 的上界只能是 read_pos-keep_hot(L1)或整文件曲终(L0)。
*
* 分层:只依赖 pthread + meow_log + POSIX(posix_fadvise),不依赖 core/App 其它 TU。
* 自含:refault 滚窗 + 三级回退(放大 keep_hot → 降 L0 → 关 A)。
*/
#include "include/meow_cachegov.h"
#include <fcntl.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
/* meow_ui.h:SDK 公共伞头(含 meow_log/meow_ui_log.h + 公共类型)。 */
#include "meow_ui.h"
/* 共享协议头:C 侧状态文件路径(meow_svc_perf 写入 / 各 App 读取,同源防漂移)。 */
#include "meow_ipc.h"
#define MEOW_CACHEGOV_MAXREG 8 /* 同时治理的缓存文件上限(ncm 单点播放,远小于此) */
#define MEOW_CACHEGOV_REFAULTRING 64 /* refault 滚窗环形缓冲 */
#define MEOW_CACHEGOV_WINDOW_MS 10000 /* refault 滚窗 10s */
#define MEOW_CACHEGOV_HOTESCALATE 1 /* 回退档>=1 时 keep_hot 放大倍数(翻倍) */
#define MEOW_CACHEGOV_DROP_THROTTLE (64u * 1024u) /* L1 每次至少推进 64KB 才再 drop(省 open/fadvise) */
typedef struct cg_entry {
char path[512];
uint64_t size; /* 标称大小(流式=nominal;命中=实际) */
uint64_t (*read_pos_fn)(void *ctx);
void *ctx;
uint64_t dropped_to; /* 已 DONTNEED 的已读前缀上界(只推进,不回退) */
int used;
} cg_entry_t;
static pthread_mutex_t g_mu = PTHREAD_MUTEX_INITIALIZER;
static struct meow_cachegov_cfg g_cfg = {
.enabled = 1,
.cached_threshold_kb = 0, /* C 归属 svc_perf,本库只读 */
.dirty_warn_kb = 0, /* 同上 */
.keep_hot_bytes = (256u * 1024u), /* 保 256KB 解码窗口供倒带 */
.min_hot_ahead = (0), /* L2 红线防御值;绝不参与 drop */
.drop_mode = 0, /* 默认仅曲终(L0);L1 需显式开(带 seek 回退) */
.refault_backoff = 3, /* 10s 内 3 次 refault → 回退 */
};
static cg_entry_t g_reg[MEOW_CACHEGOV_MAXREG];
static int g_backoff = 0; /* 0=正常 1=keep_hot放大 2=降L0 3=A关 */
static int64_t g_last_step_ms = -1000000000000LL; /* 上次升/降档时刻(初始很远 → 首次可动作) */
static int64_t g_refault_ring[MEOW_CACHEGOV_REFAULTRING];
static int g_refault_head = 0;
static int g_refault_n = 0;
/* ---- 时钟(宿主测试桩可注入) ---- */
static int64_t g_test_now_ms = -1;
static int64_t now_ms(void)
{
if (g_test_now_ms >= 0) return g_test_now_ms;
return (int64_t)meow_ui_time_ms();
}
void meow_cachegov_test_set_time_ms(int64_t t) { g_test_now_ms = t; }
/* ---- C 侧状态文件(svc_perf 写 / 本库读) ---- */
static const char *c_file(void)
{
const char *e = getenv("MEOW_CACHEGOV_C_FILE");
return (e && e[0]) ? e : MEOW_CACHEGOV_C_FILE;
}
/* C 状态(svc_perf 写)最近一次解析缓存:下载 cb 每 4KB 块会查一次,避免每块都读文件(200ms TTL)。 */
static int g_cached_c_dl = 0, g_cached_c_pf = 0;
static int64_t g_cached_c_at = 0;
/* 读 C 状态文件(缺省 0=不降载)。解析失败/文件不存在 → 全部 0。 */
static void read_c_state(int *dl_pause, int *pf_pause, long *cached_kb, long *dirty_kb)
{
if (dl_pause) *dl_pause = 0;
if (pf_pause) *pf_pause = 0;
if (cached_kb) *cached_kb = 0;
if (dirty_kb) *dirty_kb = 0;
FILE *f = fopen(c_file(), "r");
if (!f) return;
char line[160];
while (fgets(line, sizeof line, f)) {
if (!strncmp(line, "cached_kb=", 10)) { if (cached_kb) *cached_kb = atol(line + 10); }
else if (!strncmp(line, "dirty_kb=", 9)) { if (dirty_kb) *dirty_kb = atol(line + 9); }
else if (!strncmp(line, "dl_pause=", 9)) { if (dl_pause) *dl_pause = atol(line + 9) ? 1 : 0; }
else if (!strncmp(line, "prefetch_pause=", 15)) { if (pf_pause) *pf_pause = atol(line + 15) ? 1 : 0; }
}
fclose(f);
}
/* ---- refault 滚窗 ---- */
void meow_cachegov_note_refault(void)
{
int64_t t = now_ms();
pthread_mutex_lock(&g_mu);
g_refault_ring[g_refault_head] = t;
g_refault_head = (g_refault_head + 1) % MEOW_CACHEGOV_REFAULTRING;
if (g_refault_n < MEOW_CACHEGOV_REFAULTRING) g_refault_n++;
pthread_mutex_unlock(&g_mu);
}
static int refaults_last_window(void)
{
int64_t now = now_ms();
int cnt = 0, start = (g_refault_head - g_refault_n + MEOW_CACHEGOV_REFAULTRING) %
MEOW_CACHEGOV_REFAULTRING;
for (int i = 0; i < g_refault_n; i++) {
int64_t t = g_refault_ring[(start + i) % MEOW_CACHEGOV_REFAULTRING];
if (t >= now - MEOW_CACHEGOV_WINDOW_MS) cnt++;
}
return cnt;
}
/* 曲终/cur_finished 后:整文件 L0 DONTNEED。 */
int meow_cachegov_drop_finished(const char *path)
{
if (!path) return -1;
pthread_mutex_lock(&g_mu);
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++) {
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) {
g_reg[i].used = 0;
break;
}
}
int lbf = g_backoff, en = g_cfg.enabled;
pthread_mutex_unlock(&g_mu);
if (!en || lbf >= 3) { /* A 已整体关闭:连曲终 L0 也不丢(§2.3 步骤 3) */
meow_log(MEOW_LOG_INFO, "[cachegov] drop_finished skip %s (A off, backoff=%d)", path, lbf);
return 0;
}
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
/* len=0 → 到 EOF(整文件)。曲终播放器已不再读该文件,重播=冷缓存首读(等价未命中)。 */
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
close(fd);
meow_log(MEOW_LOG_INFO, "[cachegov] drop_finished L0 %s", path);
return 0;
}
/* L1:丢已读前缀 [dropped_to, read_pos-keep_hot)。返回 1=有丢。 */
static int entry_drop_l1(cg_entry_t *e, size_t keep)
{
if (!e->read_pos_fn) return 0;
uint64_t pos = e->read_pos_fn(e->ctx);
if (pos <= keep) return 0; /* keep_hot 覆盖整个已读前缀,无需丢 */
uint64_t drop_end = pos - (uint64_t)keep; /* L1 上界:read_pos 前方绝不丢(L2 红线) */
if (drop_end <= e->dropped_to) return 0; /* 无推进(seek 回退 / 前进不足) */
if (drop_end - e->dropped_to < MEOW_CACHEGOV_DROP_THROTTLE && e->dropped_to != 0)
return 0; /* 节流:不足 64KB 不重复 open */
int fd = open(e->path, O_RDONLY);
if (fd < 0) return 0;
off_t off = (off_t)e->dropped_to;
off_t len = (off_t)(drop_end - e->dropped_to);
ssize_t r = posix_fadvise(fd, off, len, POSIX_FADV_DONTNEED);
close(fd);
if (r != 0) return 0;
e->dropped_to = drop_end;
meow_log(MEOW_LOG_INFO, "[cachegov] L1 drop %s off=%lld len=%lld (pos=%lld keep=%zu)",
e->path, (long long)off, (long long)len, (long long)pos, keep);
return 1;
}
int meow_cachegov_tick(void)
{
pthread_mutex_lock(&g_mu);
int64_t now = now_ms();
/* ① 回退评估(滚窗 10s):refault 超门限 → 升一档;滚窗干净 ≥10s → 降一档。
* 每升降一档需间隔 ≥10s(逐级回退,不因窗内持续 refault 一次跳过多档,防误回退)。 */
int cnt = refaults_last_window();
if (cnt >= g_cfg.refault_backoff && g_cfg.refault_backoff > 0 && g_backoff < 3 &&
(now - g_last_step_ms) >= MEOW_CACHEGOV_WINDOW_MS) {
g_backoff = g_backoff + 1;
g_last_step_ms = now;
meow_log(MEOW_LOG_INFO, "[cachegov] backoff -> %d (refault=%d/%lds)",
g_backoff, cnt, MEOW_CACHEGOV_WINDOW_MS / 1000);
} else if (cnt == 0 && g_backoff > 0 &&
(now - g_last_step_ms) >= MEOW_CACHEGOV_WINDOW_MS) {
g_backoff = g_backoff - 1;
g_last_step_ms = now;
meow_log(MEOW_LOG_INFO, "[cachegov] backoff -> %d (clean window)", g_backoff);
}
size_t keep = g_cfg.keep_hot_bytes;
if (g_backoff >= MEOW_CACHEGOV_HOTESCALATE) keep = g_cfg.keep_hot_bytes * 2; /* 档1:放大 keep_hot */
int l1_on = g_cfg.enabled && g_cfg.drop_mode >= 1 && g_backoff < 2; /* 档2:L1 关 */
int en = g_cfg.enabled;
/* ② L1 丢弃 */
int dropped = 0;
if (en && l1_on) {
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++)
if (g_reg[i].used) dropped += entry_drop_l1(&g_reg[i], keep);
}
pthread_mutex_unlock(&g_mu);
return dropped;
}
/* ---- 注册 / 注销 ---- */
int meow_cachegov_register(const char *path, uint64_t size,
uint64_t (*read_pos_fn)(void *ctx), void *ctx)
{
if (!path) return -1;
pthread_mutex_lock(&g_mu);
int slot = -1;
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++) {
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) { slot = i; break; }
if (!g_reg[i].used && slot < 0) slot = i;
}
if (slot < 0) { pthread_mutex_unlock(&g_mu); return -1; }
snprintf(g_reg[slot].path, sizeof g_reg[slot].path, "%s", path);
g_reg[slot].size = size;
g_reg[slot].read_pos_fn = read_pos_fn;
g_reg[slot].ctx = ctx;
g_reg[slot].dropped_to = 0;
g_reg[slot].used = 1;
pthread_mutex_unlock(&g_mu);
meow_log(MEOW_LOG_INFO, "[cachegov] register %s size=%llu", path, (unsigned long long)size);
return 0;
}
int meow_cachegov_unregister(const char *path)
{
if (!path) return -1;
pthread_mutex_lock(&g_mu);
int rc = -1;
for (int i = 0; i < MEOW_CACHEGOV_MAXREG; i++)
if (g_reg[i].used && strcmp(g_reg[i].path, path) == 0) { g_reg[i].used = 0; rc = 0; break; }
pthread_mutex_unlock(&g_mu);
return rc;
}
/* ---- 降载查询(A 回退 OR svc_perf C) ---- */
static void c_state(int *dl_pause, int *pf_pause)
{
int64_t now = now_ms();
pthread_mutex_lock(&g_mu);
if (g_cached_c_at == 0 || (now - g_cached_c_at) > 200) {
read_c_state(&g_cached_c_dl, &g_cached_c_pf, NULL, NULL);
g_cached_c_at = now;
}
if (dl_pause) *dl_pause = g_cached_c_dl;
if (pf_pause) *pf_pause = g_cached_c_pf;
pthread_mutex_unlock(&g_mu);
}
int meow_cachegov_download_paused(void)
{
pthread_mutex_lock(&g_mu);
int a = (g_backoff >= 2);
pthread_mutex_unlock(&g_mu);
int c = 0;
c_state(&c, NULL);
return (a || c) ? 1 : 0;
}
int meow_cachegov_prefetch_paused(void)
{
pthread_mutex_lock(&g_mu);
int a = (g_backoff >= 3);
pthread_mutex_unlock(&g_mu);
int c = 0;
c_state(NULL, &c);
return (a || c) ? 1 : 0;
}
/* ---- 配置 ---- */
int meow_cachegov_set_cfg(const struct meow_cachegov_cfg *cfg)
{
if (!cfg) return -1;
pthread_mutex_lock(&g_mu);
g_cfg = *cfg;
pthread_mutex_unlock(&g_mu);
return 0;
}
int meow_cachegov_get_cfg(struct meow_cachegov_cfg *cfg)
{
if (!cfg) return -1;
pthread_mutex_lock(&g_mu);
*cfg = g_cfg;
pthread_mutex_unlock(&g_mu);
return 0;
}
int meow_cachegov_backoff_level(void)
{
pthread_mutex_lock(&g_mu);
int v = g_backoff;
pthread_mutex_unlock(&g_mu);
return v;
}