feat(bt): 批①宽限期补充 — 全App入后台表(bg_since_ms)+未声明App宽限30min(MEOW_BG_GRACE_MS)超时回收+声明常驻/媒体活跃不清+槽满LRU回收最旧未声明;test_bgplay全过/0warning;devdocs APP_BACKGROUND_KEEPALIVE_P1 §9;M6.34需先重编meow_ui.a(meow_img_pixels)
This commit is contained in:
1 parent
effce6d0db
commit
45c633868f
4 files changed
+264
-62
No files matched your search
@@ -7,6 +7,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
@@ -79,30 +80,100 @@ static meow_bg_entry_t *bg_find(const char *app_id)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* 填一槽(add 与槽满 LRU 复用)。 */
|
||||
static void bg_fill(int i, const char *app_id, pid_t pid, int out_fd, int on_card, int keepalive)
|
||||
{
|
||||
meow_bg_entry_t *e = &g_bg[i];
|
||||
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->used = 1;
|
||||
snprintf(e->app_id, sizeof(e->app_id), "%s", app_id);
|
||||
e->pid = pid;
|
||||
e->out_fd = out_fd;
|
||||
e->on_card = on_card;
|
||||
e->bg_since_ms = bg_now_ms(); /* 批①:转后台时刻(宽限/槽满 LRU 掐最久用的锚点) */
|
||||
e->keepalive = keepalive ? 1 : 0; /* 批①+宽限:1=声明常驻(无限期);0=宽限期类 */
|
||||
g_change_seq++;
|
||||
bg_log("[bgplay] 转后台保活: %s pid=%d on_card=%d keepalive=%d",
|
||||
app_id, (int)pid, on_card, e->keepalive);
|
||||
}
|
||||
|
||||
/* 真实单调毫秒(不随 ops.now_ms 注入的假时钟走)。槽满 LRU 回收需要真实的有界等超时——
|
||||
* 注入假时钟的宿主测试若用它算 deadline 则永不超时(假时钟不随 usleep 推进),故独立取真时钟。 */
|
||||
static uint64_t bg_real_now_ms(void)
|
||||
{
|
||||
struct timespec ts;
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
|
||||
}
|
||||
|
||||
/* 槽满 LRU 回收:找 bg_since_ms 最旧的**未声明且媒体不活跃**槽,走真退序列 SIGTERM→有界等→
|
||||
* SIGKILL→reap→on_bg_dead(通知 amsd + 清媒体会话)→ 摘表。返回被回收槽下标;-1=无可回收
|
||||
* (全为声明常驻/媒体活跃,按保安全拒绝新入)。 */
|
||||
static int bg_evict_lru(void)
|
||||
{
|
||||
uint64_t oldest = UINT64_MAX;
|
||||
int cand = -1;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MEOW_BG_MAX; i++) {
|
||||
if (!g_bg[i].used) continue;
|
||||
if (g_bg[i].keepalive) continue; /* 声明常驻不回收 */
|
||||
if (g_ops.media_resident && g_ops.media_resident(g_bg[i].app_id))
|
||||
continue; /* 媒体活跃不回收 */
|
||||
if (g_bg[i].bg_since_ms < oldest) { oldest = g_bg[i].bg_since_ms; cand = i; }
|
||||
}
|
||||
if (cand < 0) return -1;
|
||||
|
||||
{
|
||||
meow_bg_entry_t *e = &g_bg[cand];
|
||||
char id[MEOW_APP_ID_MAX];
|
||||
pid_t pid = e->pid;
|
||||
int ofd = e->out_fd;
|
||||
int st = 0;
|
||||
uint64_t deadline;
|
||||
|
||||
snprintf(id, sizeof(id), "%s", e->app_id);
|
||||
bg_log("[bgplay] 后台表满 LRU 回收: %s pid=%d(最久未激活·未声明)", id, (int)pid);
|
||||
kill(pid, SIGTERM);
|
||||
deadline = bg_real_now_ms() + TERM_GRACE_MS;
|
||||
for (;;) {
|
||||
pid_t r = waitpid(pid, &st, WNOHANG);
|
||||
if (r == pid) break;
|
||||
if (r < 0 && errno != EINTR) break; /* 已回收/非子进程:不再等 */
|
||||
if (bg_real_now_ms() >= deadline) {
|
||||
kill(pid, SIGKILL);
|
||||
deadline = bg_real_now_ms() + 200u; /* SIGKILL 后非阻塞兜底,防 D-state 挂死 */
|
||||
while (bg_real_now_ms() < deadline) {
|
||||
if (waitpid(pid, &st, WNOHANG) == pid) break;
|
||||
usleep(10000);
|
||||
}
|
||||
break;
|
||||
}
|
||||
usleep(5000);
|
||||
}
|
||||
if (ofd >= 0) close(ofd); /* 被回收 App 已死,其 stdout 读端一并关 */
|
||||
if (g_ops.on_bg_dead) g_ops.on_bg_dead(id, pid, st); /* 通知 amsd + 清媒体会话 */
|
||||
g_change_seq++;
|
||||
bg_log("[bgplay] LRU 回收完成: %s 槽释放", id);
|
||||
memset(e, 0, sizeof(*e));
|
||||
}
|
||||
return cand;
|
||||
}
|
||||
|
||||
int meow_bg_add(const char *app_id, pid_t pid, int out_fd, int on_card, int keepalive)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!app_id || !app_id[0] || pid <= 0) return MEOW_ERR_INVAL;
|
||||
if (bg_find(app_id)) return MEOW_ERR_EXISTS;
|
||||
for (i = 0; i < MEOW_BG_MAX; i++) {
|
||||
if (!g_bg[i].used) {
|
||||
meow_bg_entry_t *e = &g_bg[i];
|
||||
|
||||
memset(e, 0, sizeof(*e));
|
||||
e->used = 1;
|
||||
snprintf(e->app_id, sizeof(e->app_id), "%s", app_id);
|
||||
e->pid = pid;
|
||||
e->out_fd = out_fd;
|
||||
e->on_card = on_card;
|
||||
e->bg_since_ms = bg_now_ms(); /* 批①:转后台时刻 */
|
||||
e->keepalive = keepalive ? 1 : 0; /* 批①:常驻类型标记 */
|
||||
g_change_seq++;
|
||||
bg_log("[bgplay] 转后台保活: %s pid=%d on_card=%d keepalive=%d",
|
||||
app_id, (int)pid, on_card, e->keepalive);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < MEOW_BG_MAX; i++)
|
||||
if (!g_bg[i].used) { bg_fill(i, app_id, pid, out_fd, on_card, keepalive); return 0; }
|
||||
/* 槽满:LRU 回收最久未激活的未声明槽;全为声明常驻/媒体活跃 → NOMEM(调用方回落真退)。 */
|
||||
if (bg_evict_lru() >= 0)
|
||||
for (i = 0; i < MEOW_BG_MAX; i++)
|
||||
if (!g_bg[i].used) { bg_fill(i, app_id, pid, out_fd, on_card, keepalive); return 0; }
|
||||
return MEOW_ERR_NOMEM;
|
||||
}
|
||||
|
||||
@@ -158,25 +229,39 @@ void meow_bg_tick(void)
|
||||
|
||||
if (!e->used) continue;
|
||||
|
||||
/* 卡拔例外:二进制在卡上 → SIGTERM(防 SIGBUS),500ms 未死 SIGKILL */
|
||||
if (e->on_card && !bg_card_present()) {
|
||||
if (e->term_at_ms == 0) {
|
||||
bg_log("[bgplay] 卡拔出,终止卡上后台 App: %s pid=%d",
|
||||
e->app_id, (int)e->pid);
|
||||
/* 触发终止(SIGTERM→SIGKILL 真退序列)的两类条件,仅当尚未进入终止序列(term_at_ms==0):
|
||||
* a) 卡拔例外:on_card 且卡不在(防 SIGBUS);
|
||||
* b) 批①+宽限 超时回收:未声明(keepalive=0)且媒体不活跃(media_resident=0),转后台
|
||||
* 超 MEOW_BG_GRACE_MS 未再启动 → 回收。声明常驻(keepalive=1)/媒体活跃**不清**。 */
|
||||
if (e->term_at_ms == 0) {
|
||||
int grace = (e->keepalive == 0 &&
|
||||
!(g_ops.media_resident ? g_ops.media_resident(e->app_id) : 0) &&
|
||||
now - e->bg_since_ms > (uint64_t)MEOW_BG_GRACE_MS);
|
||||
if ((e->on_card && !bg_card_present()) || grace) {
|
||||
if (grace)
|
||||
bg_log("[bgplay] 宽限期超时回收后台App: %s pid=%d(超 %ums 未再启动)",
|
||||
e->app_id, (int)e->pid, (unsigned)MEOW_BG_GRACE_MS);
|
||||
else
|
||||
bg_log("[bgplay] 卡拔出,终止卡上后台 App: %s pid=%d",
|
||||
e->app_id, (int)e->pid);
|
||||
kill(e->pid, SIGTERM);
|
||||
e->term_at_ms = now;
|
||||
} else if (now - e->term_at_ms >= TERM_GRACE_MS) {
|
||||
kill(e->pid, SIGKILL);
|
||||
e->term_at_ms = now; /* 500ms 节流重复 SIGKILL 直至 reap */
|
||||
}
|
||||
}
|
||||
/* SIGTERM 后 SIGKILL 兜底(TERM_GRACE_MS 节流重复发直至 reap;卡拔/宽限/槽满回收共用) */
|
||||
if (e->term_at_ms && now - e->term_at_ms >= TERM_GRACE_MS) {
|
||||
kill(e->pid, SIGKILL);
|
||||
e->term_at_ms = now;
|
||||
}
|
||||
|
||||
r = waitpid(e->pid, &st, WNOHANG);
|
||||
if (r == e->pid) {
|
||||
char id[MEOW_APP_ID_MAX];
|
||||
pid_t pid = e->pid;
|
||||
int ofd = e->out_fd;
|
||||
|
||||
snprintf(id, sizeof(id), "%s", e->app_id);
|
||||
if (ofd >= 0) close(ofd); /* 回收:后台 App 已死,其 stdout 管道读端一并关(防 fd 泄漏) */
|
||||
memset(e, 0, sizeof(*e));
|
||||
g_change_seq++;
|
||||
bg_log("[bgplay] 后台 App 退出: %s pid=%d status=0x%x", id, (int)pid, st);
|
||||
|
||||
Reference in new issue
Block a user