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
@@ -479,6 +479,7 @@ void meow_bgplay_launcher_init(void)
|
||||
ops.now_ms = NULL; /* 内部 CLOCK_MONOTONIC */
|
||||
ops.card_present = sd_device_present;
|
||||
ops.on_bg_dead = bg_on_dead;
|
||||
ops.media_resident = meow_media_app_resident; /* 批①+宽限:媒体活跃判定(宽限回收/槽满LRU) */
|
||||
ops.on_svc_dead = NULL; /* 服务消亡日志 meow_bgplay 内已记 */
|
||||
ops.notif_clear_app = bg_notif_clear_app;
|
||||
ops.send_frame = bg_send_frame;
|
||||
@@ -1964,22 +1965,29 @@ static int launch_app_chain(struct app *a, const char *chain)
|
||||
break;
|
||||
}
|
||||
if (meow_nav_exit_pending()) {
|
||||
/* 批①后台保活:声明 background_keepalive → 恒保活;或声明 background_audio 且该 App
|
||||
* 媒体会话活跃(PLAYING/PAUSED/BUFFER,见 meow_media_app_resident)→ 保活。进程+音频流
|
||||
* 保留转后台(UI 冻结走 §10.5 SDK present 自抑,**非 SIGSTOP**——SIGSTOP 会冻结同进程
|
||||
* 播放线程断供 PCM,§10.5 真机实证)。登记失败(槽满/已存在)回落下方真退序列。
|
||||
* 卡拔例外不适用保活(bg 表 tick 照杀)。 */
|
||||
if (meow_bg_should_keep(a->background_audio == '1',
|
||||
meow_media_app_resident(a->id),
|
||||
a->background_keepalive == '1') &&
|
||||
meow_bg_add(a->id, pid, out_fd, app_on_card,
|
||||
a->background_keepalive == '1') == 0) {
|
||||
logmsg("bg: app %s nav-exit -> 后台保活(活跃(播放/暂停/缓冲),pid=%d 保留,SDK present 自抑)",
|
||||
a->name, (int)pid);
|
||||
/* 批①+宽限后台保活:**全 App** nav-exit 入后台保活表(进程保留转后台,UI 冻结走 §10.5
|
||||
* SDK present 自抑,**非 SIGSTOP**——SIGSTOP 会冻结同进程播放线程断供 PCM,§10.5 实证)。
|
||||
* 分两类:
|
||||
* - 声明常驻(background_keepalive=1)或媒体活跃(background_audio+PLAYING/PAUSED/BUFFER,
|
||||
* 见 meow_media_app_resident)→ **无限期保活**(不清,除非用户手动/一键清);
|
||||
* - 其余(未声明/媒体不活跃)→ **宽限期**(bg_grace_ms,默认30min)内保活,超时未再启动
|
||||
* 由 meow_bg_tick 走真退序列回收。
|
||||
* meow_bg_add 内含槽满 LRU 回收(最久未激活的未声明槽);全为声明常驻/媒体活跃 → NOMEM
|
||||
* 回落下方真退序列(保安全)。kill 通道带 !a->framework 门控(批③)。 */
|
||||
int bg_resident = meow_media_app_resident(a->id);
|
||||
int r = meow_bg_add(a->id, pid, out_fd, app_on_card,
|
||||
a->background_keepalive == '1');
|
||||
if (r == 0 || r == MEOW_ERR_EXISTS) { /* 0=入表;EXISTS=本就驻留(幂等不重复登记不杀) */
|
||||
logmsg("bg: app %s nav-exit -> 后台保活(%s,pid=%d 保留,SDK present 自抑)",
|
||||
a->name,
|
||||
((a->background_keepalive == '1') || bg_resident) ?
|
||||
"声明常驻/媒体活跃(无限期)" : "宽限期(默认30min)",
|
||||
(int)pid);
|
||||
to_background = 1;
|
||||
break;
|
||||
}
|
||||
logmsg("nav: app %s MSG_NAV{EXIT} -> terminate, return to home", a->name);
|
||||
logmsg("nav: app %s MSG_NAV{EXIT} -> terminate, return to home(后台表满且无未声明槽可回收)",
|
||||
a->name);
|
||||
nav_clean_exit = 1;
|
||||
kill(pid, SIGTERM);
|
||||
usleep(300000);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -29,6 +29,12 @@
|
||||
|
||||
/* 后台保活 App 上限(同时驻留后台的播放中 App) */
|
||||
#define MEOW_BG_MAX 4
|
||||
/* 批①+宽限:无声明/媒体不活跃 App 入后台的宽限期(默认 30 分钟)。
|
||||
* 语义:转后台(bg_since_ms)后超 MEOW_BG_GRACE_MS 未再启动 → meow_bg_tick 走真退序列
|
||||
* SIGTERM→SIGKILL 回收(摘表 + 通知 amsd)。**声明常驻(keepalive=1)或媒体活跃
|
||||
* (meow_media_app_resident=1)不清**。本批为编译期常量(30*60*1000 = 1800000ms);
|
||||
* 后续可进设置/按 App 可配(待拍板)。 */
|
||||
#define MEOW_BG_GRACE_MS 1800000u
|
||||
/* 无头后台服务运行时槽上限(与 meow_acct.svcs 的 MEOW_MAX_SVCS 对齐) */
|
||||
#define MEOW_SVC_RT_MAX 8
|
||||
|
||||
@@ -45,9 +51,10 @@ typedef struct meow_bg_entry {
|
||||
pid_t pid;
|
||||
int out_fd;
|
||||
int on_card; /* 二进制在 TF 卡上(卡拔需 SIGTERM 防 SIGBUS) */
|
||||
uint64_t term_at_ms; /* 卡拔 SIGTERM 发出时刻(0=未发;SIGKILL 兜底计时) */
|
||||
uint64_t bg_since_ms; /* 批①:转后台时刻(monotonic ms,批③管理页/闲置回收用) */
|
||||
char keepalive; /* 批①:1=background_keepalive 类(无媒体会话也保活);0=background_audio 类 */
|
||||
uint64_t term_at_ms; /* 卡拔/宽限超时/槽满回收 SIGTERM 发出时刻(0=未发;SIGKILL 兜底计时) */
|
||||
uint64_t bg_since_ms; /* 批①:转后台时刻(monotonic ms;宽限超时回收/槽满 LRU 掐最久用) */
|
||||
char keepalive; /* 批①+宽限:1=声明常驻(background_keepalive,无限期不清);0=宽限期类
|
||||
* (未声明/background_audio,媒体不活跃时超 bg_grace_ms 回收) */
|
||||
} meow_bg_entry_t;
|
||||
|
||||
/* 无头后台服务运行时槽(静态声明在 meow_acct.svcs,此处为运行态)。 */
|
||||
@@ -73,6 +80,8 @@ typedef struct meow_svc_rt {
|
||||
/* 依赖注入。全 NULL 可用(log 静默、now_ms 内部 CLOCK_MONOTONIC、card_present 恒 1、
|
||||
* 通知清理/发帧跳过)。
|
||||
* on_bg_dead:后台保活 App 进程退出(status=waitpid 原始状态;正常/异常由回调方判);
|
||||
* media_resident:该 App 媒体会话是否「活跃可驻留」(launcher 注入 meow_media_app_resident)。
|
||||
* 批①+宽限用:宽限超时回收 / 槽满 LRU 判「媒体活跃不清不回收」;NULL=恒 0(视为不活跃)。
|
||||
* on_svc_dead:服务槽最终消亡(stopping 走完/不重启死亡;熔断不算消亡,槽保留待复位);
|
||||
* notif_clear_app:熔断时清该 App 全部通知,返回清除条数(<0=失败);
|
||||
* send_frame:熔断广播 MSG_SYS(launcher 侧须先查连接存活再发);
|
||||
@@ -82,6 +91,7 @@ typedef struct meow_bgplay_ops {
|
||||
uint64_t (*now_ms)(void);
|
||||
int (*card_present)(void);
|
||||
void (*on_bg_dead)(const char *app_id, pid_t pid, int status);
|
||||
int (*media_resident)(const char *app_id);
|
||||
void (*on_svc_dead)(const char *app_id, const char *svc);
|
||||
int (*notif_clear_app)(const char *app_id);
|
||||
size_t (*send_frame)(int fd, uint16_t type, uint32_t req_id,
|
||||
@@ -101,8 +111,11 @@ void meow_bgplay_init(const meow_bgplay_ops *ops);
|
||||
int meow_bg_should_keep(int bg_audio_declared, int media_resident, int keepalive_declared);
|
||||
|
||||
/* ---- 后台保活 App 表 ---- */
|
||||
/* 转后台:登记 app_id/pid/out_fd,keepalive 标记常驻类型(1=background_keepalive 类)。
|
||||
* 返回 0=成功;MEOW_ERR_EXISTS=已在后台;MEOW_ERR_NOMEM=满。 */
|
||||
/* 转后台:登记 app_id/pid/out_fd,keepalive 标记常驻类型(1=background_keepalive 类=无限期;
|
||||
* 0=宽限期类=未声明/background_audio,媒体不活跃时超 bg_grace_ms 回收)。批①+宽限:**全 App**
|
||||
* nav-exit 都经此入表;槽满(MEOW_BG_MAX=4)时 LRU 回收最久未激活(bg_since_ms 最旧)的**未声明**
|
||||
* 槽(不回收声明常驻/媒体活跃);全为声明常驻/媒体活跃 → MEOW_ERR_NOMEM(调用方回落真退,保安全)。
|
||||
* 返回 0=成功;MEOW_ERR_EXISTS=已在后台;MEOW_ERR_NOMEM=满且无可回收。 */
|
||||
int meow_bg_add(const char *app_id, pid_t pid, int out_fd, int on_card, int keepalive);
|
||||
/* 回前台:按 app_id 摘除槽(进程归还前台会话)。成功返回 0 且 *out_fd(可 NULL)带回
|
||||
* 管道读端;未在后台返回 -1。 */
|
||||
@@ -116,8 +129,11 @@ const meow_bg_entry_t *meow_bg_at(int i);
|
||||
/* 表变化计数(add/take/回收 递增);菜单 1s tick 比对刷新「·后台」标注用。 */
|
||||
uint32_t meow_bgplay_change_seq(void);
|
||||
|
||||
/* 周期调(主循环 ~10ms 排空点):waitpid WNOHANG 回收死亡后台 App(→on_bg_dead 清槽);
|
||||
* 卡拔例外:on_card 槽在卡不在时 SIGTERM,500ms 未死 SIGKILL。 */
|
||||
/* 周期调(主循环 ~10ms 排空点):waitpid WNOHANG 回收死亡后台 App(→on_bg_dead 清槽)。
|
||||
* 终止触发(SIGTERM,TERM_GRACE_MS 后 SIGKILL):
|
||||
* - 卡拔例外:on_card 槽在卡不在时终止(防 SIGBUS);
|
||||
* - 批①+宽限 超时回收:未声明(keepalive=0)且媒体不活跃(media_resident=0)的槽,转后台超
|
||||
* MEOW_BG_GRACE_MS 未再启动 → 回收(走真退序列 + 摘表 + 通知 amsd)。声明常驻/媒体活跃不清。 */
|
||||
void meow_bg_tick(void);
|
||||
|
||||
/* ---- 无头后台服务 ---- */
|
||||
|
||||
@@ -7,7 +7,10 @@
|
||||
* should_keep=0(停播后再退出=真退);
|
||||
* 3) 后台 App 崩溃照收:子进程被杀 → bg_tick WNOHANG 回收 → on_bg_dead + 清槽;
|
||||
* 4) 卡拔例外:on_card=1 + card_present 假 0 → tick SIGTERM 回收(防 SIGBUS);
|
||||
* 5) bg 表满(MEOW_BG_MAX)→ MEOW_ERR_NOMEM;重复 add → MEOW_ERR_EXISTS;
|
||||
* 4b) 批①+宽限:未声明(keepalive=0)媒体不活跃超 MEOW_BG_GRACE_MS 回收(含恰等边界不回收);
|
||||
* 声明常驻(keepalive=1)或媒体活跃(META PLAYING)超 grace 不清;
|
||||
* 5) bg 表满(MEOW_BG_MAX)→ LRU 回收最久未激活的未声明槽新增成功;全为声明常驻 → MEOW_ERR_NOMEM
|
||||
* (不回收声明常驻);pid<=0 → MEOW_ERR_INVAL;重复 add → MEOW_ERR_EXISTS;
|
||||
* 6) 服务注册:exec 不可执行 → NOENT;同名在跑 → EXISTS;
|
||||
* 7) 退避重启 1s/2s + max_restart=2 熔断(假时钟注入):清通知回调 + 注册连接收到
|
||||
* MSG_SYS{SVC_DEAD=2} + circuit 后不再拉起;重注册复位熔断重新拉起;
|
||||
@@ -154,6 +157,7 @@ int main(void)
|
||||
ops.now_ms = fake_now_ms;
|
||||
ops.card_present = fake_card;
|
||||
ops.on_bg_dead = fake_bg_dead;
|
||||
ops.media_resident = meow_media_app_resident; /* 批①+宽限:媒体活跃判定(宽限回收/槽满LRU) */
|
||||
ops.notif_clear_app = fake_notif_clear;
|
||||
ops.send_frame = fake_send_frame;
|
||||
ops.log_dir = NULL;
|
||||
@@ -267,33 +271,122 @@ int main(void)
|
||||
"卡拔 → 卡上后台 App SIGTERM 回收(防 SIGBUS)");
|
||||
g_card = 1;
|
||||
|
||||
/* ---- 5) 表满 / 重复 ---- */
|
||||
/* ---- 5) 表满:LRU 回收最久未激活的未声明槽 / 全常驻满→NOMEM / 重复 / 参数 ---- */
|
||||
{
|
||||
pid_t k[MEOW_BG_MAX];
|
||||
char id[MEOW_APP_ID_MAX];
|
||||
int i, full = 0;
|
||||
int i;
|
||||
pid_t gextra = 0;
|
||||
for (i = 0; i < MEOW_BG_MAX; i++) {
|
||||
k[i] = spawn_sleeper();
|
||||
snprintf(id, sizeof id, "dev.test.full%d", i);
|
||||
meow_bg_add(id, k[i], -1, 0, 0);
|
||||
snprintf(id, sizeof id, "dev.test.lru%d", i);
|
||||
CHECK(meow_bg_add(id, k[i], -1, 0, 0) == 0, "lru 填表(未声明 keepalive=0)");
|
||||
}
|
||||
snprintf(id, sizeof id, "dev.test.full%d", MEOW_BG_MAX);
|
||||
/* 5.1 表满 + 全未声明 → 新增 LRU 回收最旧(lru0,bg_since_ms 最小),新 App 入表成功 */
|
||||
{
|
||||
pid_t extra = spawn_sleeper();
|
||||
full = (meow_bg_add(id, extra, -1, 0, 0) == MEOW_ERR_NOMEM);
|
||||
kill(extra, SIGKILL); waitpid(extra, NULL, 0); /* 未入表,就地清理 */
|
||||
pid_t e2 = spawn_sleeper();
|
||||
gextra = e2;
|
||||
CHECK(meow_bg_add("dev.test.lrunew", e2, -1, 0, 0) == 0,
|
||||
"表满全未声明 → LRU 回收最旧槽并成功新增");
|
||||
CHECK(meow_bg_count() == MEOW_BG_MAX, "回收后仍满 4 槽");
|
||||
CHECK(meow_bg_is_backgrounded("dev.test.lrunew") == 1, "新增 App 在后台表");
|
||||
CHECK(meow_bg_is_backgrounded("dev.test.lru0") == 0, "最久未激活未声明 lru0 被回收摘表");
|
||||
CHECK(kill(k[0], 0) < 0, "lru0 进程已终止(SIGTERM 真退回收)");
|
||||
}
|
||||
CHECK(full, "bg 表满 → MEOW_ERR_NOMEM");
|
||||
snprintf(id, sizeof id, "dev.test.full%d", 0);
|
||||
CHECK(meow_bg_add(id, 0, -1, 0, 0) == MEOW_ERR_INVAL, "pid<=0 → INVAL");
|
||||
CHECK(meow_bg_add(id, k[0], -1, 0, 0) == MEOW_ERR_EXISTS, "重复 app_id → EXISTS");
|
||||
for (i = 0; i < MEOW_BG_MAX; i++) {
|
||||
snprintf(id, sizeof id, "dev.test.full%d", i);
|
||||
/* 5.2 重复 add → EXISTS;pid<=0 → INVAL(lru0 已回收,表内为 lrunew/lru1/lru2/lru3) */
|
||||
CHECK(meow_bg_add("dev.test.lrunew", 999999, -1, 0, 0) == MEOW_ERR_EXISTS, "重复 app_id → EXISTS");
|
||||
CHECK(meow_bg_add("dev.test.lrunew", 0, -1, 0, 0) == MEOW_ERR_INVAL, "pid<=0 → INVAL");
|
||||
/* 5.3 清空剩余(take + 杀进程防泄漏) */
|
||||
for (i = 1; i < MEOW_BG_MAX; i++) {
|
||||
snprintf(id, sizeof id, "dev.test.lru%d", i);
|
||||
meow_bg_take(id, NULL, NULL);
|
||||
kill(k[i], SIGKILL); waitpid(k[i], NULL, 0);
|
||||
}
|
||||
meow_bg_take("dev.test.lrunew", NULL, NULL);
|
||||
kill(gextra, SIGKILL); waitpid(gextra, NULL, 0);
|
||||
CHECK(meow_bg_count() == 0, "清空后 bg 表空(未声明)");
|
||||
}
|
||||
|
||||
/* ---- 5b) 表满 + 全为声明常驻(keepalive=1)→ 拒绝新入 NOMEM(不回收声明常驻,保安全) ---- */
|
||||
{
|
||||
pid_t d[MEOW_BG_MAX];
|
||||
char id[MEOW_APP_ID_MAX];
|
||||
int i;
|
||||
for (i = 0; i < MEOW_BG_MAX; i++) {
|
||||
d[i] = spawn_sleeper();
|
||||
snprintf(id, sizeof id, "dev.test.dec%d", i);
|
||||
CHECK(meow_bg_add(id, d[i], -1, 0, 1) == 0, "声明常驻填表(keepalive=1)");
|
||||
}
|
||||
{
|
||||
pid_t e2 = spawn_sleeper();
|
||||
CHECK(meow_bg_add("dev.test.decnew", e2, -1, 0, 0) == MEOW_ERR_NOMEM,
|
||||
"全声明常驻满 → 新增 NOMEM(不回退真退,保安全)");
|
||||
kill(e2, SIGKILL); waitpid(e2, NULL, 0); /* 未入表,就地清理 */
|
||||
}
|
||||
for (i = 0; i < MEOW_BG_MAX; i++) {
|
||||
snprintf(id, sizeof id, "dev.test.dec%d", i);
|
||||
meow_bg_take(id, NULL, NULL);
|
||||
kill(d[i], SIGKILL); waitpid(d[i], NULL, 0);
|
||||
}
|
||||
CHECK(meow_bg_count() == 0, "声明常驻清空后 bg 表空");
|
||||
}
|
||||
|
||||
/* ================= 批①+宽限:宽限超时回收 / 声明常驻与媒体活跃不清 ================= */
|
||||
g_ms = 100000; /* 本组从干净时间基准起(后续 svc 段用相对 g_ms,不受影响) */
|
||||
|
||||
/* 5c) 未声明 + 媒体不活跃:grace 内保活;恰等 MEOW_BG_GRACE_MS 不回收(严格 >);超界回收 */
|
||||
{
|
||||
pid_t s1 = spawn_sleeper();
|
||||
g_bg_dead = 0;
|
||||
CHECK(meow_bg_add("dev.test.grace1", s1, -1, 0, 0) == 0, "grace1 入表(未声明 keepalive=0)");
|
||||
g_ms = 100000u + MEOW_BG_GRACE_MS; /* 恰好等于宽限(非严格大于) */
|
||||
meow_bg_tick();
|
||||
CHECK(meow_bg_is_backgrounded("dev.test.grace1") == 1 && g_bg_dead == 0,
|
||||
"grace 边界:恰等 MEOW_BG_GRACE_MS 不回收(严格 > 才回收)");
|
||||
g_ms = 100000u + MEOW_BG_GRACE_MS + 1; /* 超界 1ms */
|
||||
pump_bg_until(cond_bg_dead, NULL);
|
||||
CHECK(meow_bg_is_backgrounded("dev.test.grace1") == 0 && meow_bg_count() == 0,
|
||||
"超 grace 未再启动 → 回收摘表");
|
||||
CHECK(g_bg_dead == 1, "宽限回收触发 on_bg_dead(通知 amsd)+ 进程经 SIGTERM 退出");
|
||||
}
|
||||
|
||||
/* 5d) 声明常驻(keepalive=1)超 grace 不清(无限期) */
|
||||
{
|
||||
pid_t k1 = spawn_sleeper();
|
||||
CHECK(meow_bg_add("dev.test.gracekeep", k1, -1, 0, 1) == 0, "gracekeep 入表(keepalive=1)");
|
||||
g_ms = 100000u + MEOW_BG_GRACE_MS + 100000;
|
||||
meow_bg_tick();
|
||||
CHECK(meow_bg_is_backgrounded("dev.test.gracekeep") == 1,
|
||||
"声明常驻 keepalive=1 超 grace 不清(无限期)");
|
||||
meow_bg_take("dev.test.gracekeep", NULL, NULL);
|
||||
kill(k1, SIGKILL); waitpid(k1, NULL, 0);
|
||||
}
|
||||
|
||||
/* 5e) 媒体活跃(META PLAYING,keepalive=0)超 grace 不清(resident 保护) */
|
||||
{
|
||||
int sp2[2];
|
||||
pid_t m1;
|
||||
CHECK(socketpair(AF_UNIX, SOCK_STREAM, 0, sp2) == 0, "grace media socketpair");
|
||||
{
|
||||
m_meta_t meta;
|
||||
memset(&meta, 0, sizeof meta);
|
||||
snprintf(meta.app_id, sizeof meta.app_id, "dev.test.gracemedia");
|
||||
meta.state = MEOW_MEDIA_PLAYING;
|
||||
meow_media_meta(&meta, sp2[0]);
|
||||
}
|
||||
m1 = spawn_sleeper();
|
||||
CHECK(meow_bg_add("dev.test.gracemedia", m1, -1, 0, 0) == 0,
|
||||
"gracemedia 入表(keepalive=0 但媒体活跃)");
|
||||
g_ms = 100000u + MEOW_BG_GRACE_MS + 100000;
|
||||
meow_bg_tick();
|
||||
CHECK(meow_media_app_resident("dev.test.gracemedia") == 1, "gracemedia media_resident=1");
|
||||
CHECK(meow_bg_is_backgrounded("dev.test.gracemedia") == 1,
|
||||
"媒体活跃超 grace 不清(resident 保护)");
|
||||
meow_bg_take("dev.test.gracemedia", NULL, NULL);
|
||||
kill(m1, SIGKILL); waitpid(m1, NULL, 0);
|
||||
meow_media_clear("dev.test.gracemedia");
|
||||
close(sp2[0]); close(sp2[1]);
|
||||
}
|
||||
CHECK(meow_bg_count() == 0, "take 全部后 bg 表空");
|
||||
|
||||
/* ================= 无头后台服务 ================= */
|
||||
write_test_svc();
|
||||
|
||||
Reference in new issue
Block a user