SDK: 修 meow_scroll_wrap 池槽泄漏(free 归还 wrap 上下文槽)+ 64 次回收/交错/池满回归(M19)

meow_scroll_wrap 从 MEOW_SCROLL_POOL(=8) 个 wrap 上下文槽占一个,meow_scroll_free 只归还
会话槽、从不归还 wrap 槽也不清 ctx.w → 进程内最多成功 wrap 8 次,之后恒 NULL。图库
list_rebuild/load_dir 每次重建,进 8 个目录后列表丢惯性(g_scroll==NULL 走 scroll_by 兜底),
os.meow.files 同样每次 reload 重建。

- meow_scroll 结构内记 wrap_slot(创建置 -1),wrap 时写入槽下标;free 时按该下标清
  g_wrap_ctx[].w/lv 并归还 g_wrap_used,随后置 -1(二次 free 不触碰任何槽)。
  直接 create 的实例 wrap_slot=-1,free 不误清别人的槽。
- 池满语义不变:8 个同时活跃时第 9 个 wrap 仍返回 NULL(不越界);free(NULL) 安全。
- test_meow_ui_scroll 补回归:64 次 wrap/free 全可用(修复前 8/64,修复后 64/64)、free 后
  再 wrap、8 满 + 第 9 个 NULL + 释放其一即复用、两 wrap 交错 free 其一不串槽、直接 create
  不误清槽、list→listview→list 槽复用不残留 kind、free(NULL)/二次 free。
This commit is contained in:
AskaEth committed 2026-11-05 19:38:55 +08:00
1 parent 782a5f7c90
commit c85877b447
2 files changed
+243 -16

No files matched your search

+20 -5
View File
@@ -52,11 +52,18 @@ struct meow_scroll {
int bounce; /* fling 边界回弹幅度(带符号;0=不回弹) */
int bounce_start; /* 回弹回位起点(= clamp 边界) */
int snap_to; /* snap 目标 */
int wrap_slot; /* >=0:占用的 g_wrap_ctx 槽下标(-1=非 wrap 实例) */
};
static meow_scroll g_pool[MEOW_SCROLL_POOL];
static unsigned char g_used[MEOW_SCROLL_POOL];
/* wrap 上下文槽:meow_scroll_wrap 占用一个,meow_scroll_free 必须归还(否则进程内最多 wrap
* MEOW_SCROLL_POOL 次后恒 NULL)。槽由 wrap 实例独占,free 时按 wrap_slot 精确归还。 */
typedef struct { meow_widget *w; int lv; } wrap_ctx;
static wrap_ctx g_wrap_ctx[MEOW_SCROLL_POOL];
static int g_wrap_used[MEOW_SCROLL_POOL];
/* ---- 生命周期 ---- */
meow_scroll *meow_scroll_create(meow_scroll_set_fn set, meow_scroll_max_fn max,
meow_scroll_get_fn get, meow_scroll_redraw_fn redraw,
@@ -70,6 +77,7 @@ meow_scroll *meow_scroll_create(meow_scroll_set_fn set, meow_scroll_max_fn max,
memset(s, 0, sizeof *s);
s->set = set; s->max = max; s->get = get; s->redraw = redraw; s->ctx = ctx;
s->tau_ms = MEOW_SCROLL_TAU;
s->wrap_slot = -1;
return s;
}
}
@@ -81,11 +89,20 @@ void meow_scroll_free(meow_scroll *s)
if (s->anim) meow_anim_cancel(s->anim);
s->anim = NULL;
for (int i = 0; i < MEOW_SCROLL_POOL; i++)
if (&g_pool[i] == s) { g_used[i] = 0; return; }
if (&g_pool[i] == s) {
int ws = s->wrap_slot;
if (ws >= 0 && ws < MEOW_SCROLL_POOL) { /* wrap 实例:归还上下文槽 */
g_wrap_ctx[ws].w = NULL;
g_wrap_ctx[ws].lv = 0;
g_wrap_used[ws] = 0;
}
s->wrap_slot = -1; /* 二次 free 时不再触碰任何槽 */
g_used[i] = 0;
return;
}
}
/* ---- wrap:包一个 meow_widget 列表 ---- */
typedef struct { meow_widget *w; int lv; } wrap_ctx;
static void wrap_set(void *c, int off)
{ wrap_ctx *wc = c; if (wc->lv) meow_listview_set_scroll(wc->w, off); else meow_list_set_scroll(wc->w, off); }
static int wrap_max(void *c)
@@ -100,9 +117,6 @@ static void wrap_redraw(void *c)
if (r[2] > 0 && r[3] > 0) meow_ui_flush_region(r[0], r[1], r[2], r[3]); /* 只刷视口区 */
}
static wrap_ctx g_wrap_ctx[MEOW_SCROLL_POOL];
static int g_wrap_used[MEOW_SCROLL_POOL];
meow_scroll *meow_scroll_wrap(meow_widget *list, int is_listview)
{
if (!list) return NULL;
@@ -113,6 +127,7 @@ meow_scroll *meow_scroll_wrap(meow_widget *list, int is_listview)
g_wrap_used[i] = 1;
g_wrap_ctx[i].w = list; g_wrap_ctx[i].lv = is_listview ? 1 : 0;
s->ctx = &g_wrap_ctx[i];
s->wrap_slot = i;
return s;
}
}