a3c6e89378
道F 批 2 / task-17。定位 + 单点收口,不开 net 层跨上下文大口子。
【定位(链路,全部代码可复核)】
探针挂起 30s → time_sync.c:149 预算耗尽 → :187 net_drop_conn()。NET_MSG_
DROP_CONN 处理器(net_drop_conn_locked)内同步跑 net_sock_reset_sync
(QUARANTINE)(2ms 节奏、400ms 底/600ms 顶,自家注释原记「等待期 netTask
有界占用 ~400-600ms」):netTask 的 1ms 主循环冻结在同一迭代 → NET 位
停摆;串行总线整窗停摆 → UI 推流/FTP/104 在途消息回执顺延;对时调用方
睡在 net_wait_result 等回执期间无分片心跳 → 五位同窗缺席 → 连续 8 个
100ms 抽样停喂 → 声明 [WD] task stall(V5 时代 req=0x37、快照只剩 bit0,
即日志 mask=0x00000001)。被动断连/EOF 走 net_listener_tick 异步沉降机
(D10 条令)故纯 FTP 负载 0 stall——只有主动 drop 留在同步原语里。
【修复】监听型 fd 的芯片拆除改为 net_listener_settle(QUARANTINE) +
net_listener_request(REQ_DROP) 登记即返,拆链/排渣/等 CLOSED/重建全部
交 net_listener_tick 30ms 节奏——与被动释放同一台机器(E16 台架实证该
自建序列 RST 10/10、FIN 5/5)。非监听 client fd 无监听可保,同步形态
原样保留(全仓零生产调用者)。返回值语义显式改为「拆除与重建已登记」
(net_socket.h 注释按 lead 要求写明,防未来调用方按旧语义写)。
APP_ASSERT(S12 试点)分派门与断言位置未动,登记时序下仍成立。
【评估否决留档(lead 要求论证入文档)】「缩短重建重试 5×2s」不采:
该退避在 time_sync_open_listener() bind/listen 失败路径、已被
time_sync_delay_fed() 分片喂保护、不在缺口链上;缩短反撞 TRAP-NET-09
沉降窗。全文见 docs/网络/网络自愈架构设计.md §4.6(已改写)。
【竞态与信源核对】① 主路径置 CLOSED 后迟到的 DISCONNECT/TIMEOUT 被
net_events 状态门拦截(只认活会话态),不会把刚登记的 QUARANTINE 降级;
② 零新增读清点——全局寄存器唯一读者仍 net_poll,隔离窗两处本 Socket
读清是 net_listener_tick 原位,§4.10 三条准入判据不触发;③ E13 物理
边界(对端不配合 FIN_WAIT_2 长尾)逐字不变,只移动等待发生的位置。
【测试】net_socket 主机套件 457/457:TC-NETSOCK-240f/321b/321b2/460e
重锚到登记式(settle+REQ_DROP、rst 机零启动、tick 预算 ≤20、无 [ERR]),
新增 321g/h/i + 460f 钉住 client fd 旧同步形态。全量 --all-files 40 绿。
【体积增量(vs scripts/ram_baseline.tsv 2026-10-25 行)】
ER_IROM1 237328→237344(+16 B);RW_IRAM1 75792(+0);ZI 0。
@build 0 错误 0 警告(Code=215948 RO=21396 RW=1232 ZI=131984)。
gate: only mem-budget/mem-numbers pending lead ledger backfill
test-coverage 本条**绿**(已覆盖行净增,未动用裁决③的覆盖率例外——
旧同步路径行经 client fd 新用例继续被覆盖,净差非负)。其余含
id-check/doc-links/全部主机单测绿。仅 mem-numbers/mem-budget 两红 =
台账 +16B 漂移待回填(lead 单写者禁区)。
【越界申报】① test/host/net_socket/{ns_s3_msg,ns_s4_state,ns_s5_io}.c
(批准③同类:只动本任务相关用例,未碰他人行);② docs/网络/
网络自愈架构设计.md §4.6 改写(lead 本任务指令明示)。登记簿 N-17
销项与 README 波次行归 lead;docs/内存管理 数字未碰(禁区)。
台架:V5 复跑判据(e02_probe + FTP ≥30min 零 stall / 零复位)属网络
对端类——待 lead 排窗,步骤清单随 done 消息发出。
616 lines
26 KiB
C
616 lines
26 KiB
C
/*
|
||
* 分片 3:跨上下文消息面(net_process_messages 全分派 + 结果经任务通知值回传)
|
||
* 前置:net_socket_test.c 已纳入生产源与假内核/假芯片/兄弟模块桩(同一 TU)。
|
||
*
|
||
* 这一片是整套里最值钱的部分,钉的是 AGENTS 模块约定的两条硬契约:
|
||
* A.「所有 net_* socket 操作经 netMsgQueue 在 netTask 串行执行」
|
||
* B.「net_* 公开 API 的错误码经任务通知值回传:net_msg_t 是按值拷入队列的,
|
||
* netTask 改的是自己那份副本,调用方永远看不到 → 错误码搭 xTaskNotify 通知值回来;
|
||
* 勿给 net_msg_t 加回传字段(加了只会让调用方读到栈垃圾)」
|
||
* 断言形态因此刻意是:先看**通知值本身**(32 位整体即结果,负值=精确错误码),
|
||
* 再看调用方在自己上下文恢复出的 errno——而不是去读队列里的 msg.result
|
||
* (那正是旧实现的错觉,也是 B 条明文禁止的方向)。
|
||
*
|
||
* 创建日期:2026-10-24
|
||
*/
|
||
|
||
/* close 场景的增量快照基准(前面场景也会 clear,必须按增量断言) */
|
||
static int s_close_base_clear;
|
||
static int s_close_base_settle;
|
||
|
||
/* 造一条已建立的 TCP 会话(多数消息场景的前置) */
|
||
static int mk_established(int type)
|
||
{
|
||
int fd = net_socket(NET_AF_INET, type);
|
||
net_sock_t *p;
|
||
|
||
if (fd < 0) {
|
||
return fd;
|
||
}
|
||
p = &s_net_socks[fd];
|
||
p->state = NET_SOCK_STATE_ESTABLISHED;
|
||
p->send_ready = 1U;
|
||
p->readable = 0U;
|
||
p->local_port = 2404U;
|
||
p->remote_port = 51999U;
|
||
p->remote_ip = 0x0B01A8C0U;
|
||
return fd;
|
||
}
|
||
|
||
/* TC-NETSOCK-200 未知消息类型:枚举闭合外的值也必须给出**本条消息自身**的确定结果
|
||
* (不许把上一条的回执残留当成这次的结果) */
|
||
static void tc_200_unknown_type(void)
|
||
{
|
||
net_msg_t msg;
|
||
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = (net_msg_type_t)999;
|
||
msg.sockfd = 0;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
msg.result = 12345; /* 脏值:分派必须覆写它 */
|
||
CHECK(ns_rtos_q_put_raw(&msg) == 0, "TC-NETSOCK-200a raw message enqueued");
|
||
CHECK(g_ns_notify_calls == 0, "TC-NETSOCK-200b enqueue itself produces no notification");
|
||
|
||
net_process_messages(); /* 以 netTask 身份跑一轮 */
|
||
|
||
CHECK(g_ns_notify_calls == 1, "TC-NETSOCK-200c unknown type must still send a receipt (else the caller hangs forever)");
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR,
|
||
"TC-NETSOCK-200d notify value for unknown type = this message exact code NET_ERR, not a leftover");
|
||
CHECK(ns_rtos_q_count() == 0, "TC-NETSOCK-200e queue drained within this round (non-blocking while semantics)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-201 队列为空:四个自愈 tick 必跑,且不产生任何通知 */
|
||
static void tc_201_ticks_run_before_dispatch(void)
|
||
{
|
||
bring_up();
|
||
|
||
net_process_messages();
|
||
|
||
CHECK(g_peers.listener_tick_calls == 1 && g_peers.pending_tick_calls == 1 &&
|
||
g_peers.send_tick_calls == 1 && g_peers.reconcile_calls == 1,
|
||
"TC-NETSOCK-201a even with an empty queue all four self-heal ticks run (D10/pending x2/reconcile)");
|
||
CHECK(g_ns_notify_calls == 0 && g_ns_get_calls == 1,
|
||
"TC-NETSOCK-201b empty queue: one peek then exit, no notification");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-202 队列句柄未绑定:直接返回,连 tick 都不该跑 */
|
||
static void tc_202_no_queue_bound(void)
|
||
{
|
||
reset_all();
|
||
g_ns_pump = net_process_messages;
|
||
|
||
net_process_messages();
|
||
|
||
CHECK(g_peers.listener_tick_calls == 0 && g_ns_get_calls == 0,
|
||
"TC-NETSOCK-202 net_process_messages without a bound queue returns at once (no chip, no ticks)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-210 SEND 快路径:端到端跑通"入队 → 分派 → 写芯片 → 通知值=字节数" */
|
||
static void tc_210_send_end_to_end(void)
|
||
{
|
||
char buf[8];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
(void)memset(buf, 'A', sizeof(buf));
|
||
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_send(fd, buf, (int)sizeof(buf), 0);
|
||
|
||
CHECK(rc == (int)sizeof(buf), "TC-NETSOCK-210a net_send returns the number of bytes actually written");
|
||
CHECK(g_chip.write_calls == 1 && g_chip.last_read_len == (uint16_t)sizeof(buf),
|
||
"TC-NETSOCK-210b one full write (a healthy path neither chunks nor goes pending)");
|
||
CHECK(s_net_socks[fd].send_bytes == sizeof(buf), "TC-NETSOCK-210c sent byte statistic accumulates");
|
||
CHECK(g_ns_notify_calls == 1 && g_ns_notify_last_val == (uint32_t)sizeof(buf),
|
||
"TC-NETSOCK-210d result travels back on the notify value (the whole 32 bits are the result)");
|
||
CHECK(g_ns_notify_last_to == (TaskHandle_t)&g_ns_task_caller,
|
||
"TC-NETSOCK-210e notify goes to the REGISTERED caller only, not a broadcast");
|
||
CHECK(g_ns_block_waits == 1,
|
||
"TC-NETSOCK-210f zero-wait found nothing -> blocking branch (second stage of the two-step peek)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-211 大帧分块:超过 NET_SEND_CHUNK_MAX 必须按硬件发送缓冲上界切块
|
||
* (TRAP-NET-16:单次写 >1KB 会写穿 1KB 发送 FIFO) */
|
||
static void tc_211_send_chunking(void)
|
||
{
|
||
static char buf[3000];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
/* 造"每写完一块芯片就报 SENBUF_FREE"的健康链路:让 net_poll 把 send_ready 置回来 */
|
||
g_ns_int_pin_set = 0;
|
||
ch395f_fake_push_glob((uint16_t)(1U << (fd + 4)));
|
||
/* SENBUF_FREE 是**条件位**:发送缓冲空着就还会再报。用假件的电平型掩码而不是
|
||
* 一次性位图,否则第二块起就再也等不到空闲 → 变成测假件的清零时机。 */
|
||
ch395f_fake_set_sint_sticky((uint8_t)fd, CH395F_SINT_STAT_SENBUF_FREE);
|
||
|
||
rc = net_send(fd, buf, 2500, 0);
|
||
|
||
CHECK(rc == 2500, "TC-NETSOCK-211a all 2500 bytes delivered");
|
||
CHECK(g_chip.write_calls == 3, "TC-NETSOCK-211b split into 3 chunks at the 1024 bound (1024/1024/452)");
|
||
CHECK(g_chip.last_read_len == 452U, "TC-NETSOCK-211c last chunk writes only the remainder");
|
||
CHECK(NET_SEND_CHUNK_MAX == 1024,
|
||
"TC-NETSOCK-211d chunk size comes from the chip send buffer, not a fake-defined number");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-212 SEND 挂起登记:等发送缓冲超预算 → 登记挂起并**跳过即时回执** */
|
||
static void tc_212_send_pending_defers_notify(void)
|
||
{
|
||
static char buf[64];
|
||
net_msg_t msg;
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].send_ready = 0U; /* 芯片一直不腾缓冲 */
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_SEND;
|
||
msg.sockfd = fd;
|
||
msg.buf = buf;
|
||
msg.len = (int)sizeof(buf);
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
|
||
CHECK(ns_rtos_q_put_raw(&msg) == 0, "TC-NETSOCK-212a SEND message enqueued");
|
||
net_process_messages();
|
||
|
||
CHECK(g_peers.send_reg_calls == 1,
|
||
"TC-NETSOCK-212b past the spin budget it registers a pending send instead of busy-waiting in netTask");
|
||
CHECK(g_peers.last_snd_fd == fd && g_peers.last_snd_len == (int)sizeof(buf) &&
|
||
g_peers.last_snd_sent == 0,
|
||
"TC-NETSOCK-212c registered progress = bytes already written to the chip (0 here), length and caller travel too");
|
||
CHECK(g_ns_notify_calls == 0,
|
||
"TC-NETSOCK-212d a successful registration NEVER sends an immediate receipt (else it reads as the result)");
|
||
CHECK(g_ns_notify_last_to == NULL,
|
||
"TC-NETSOCK-212e not a single notification (the 0x70000000 sentinel can not leak into a notify value)");
|
||
|
||
/* 完成时刻:由挂起 send 的 tick 结束(本套由桩脚本模拟"缓冲腾出来了") */
|
||
ns_peers_arm_send_pending_done(fd, 64);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_calls == 1 && g_ns_notify_last_val == 64U,
|
||
"TC-NETSOCK-212f receipt only after the pending completes, notify value = bytes actually written");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-213 SEND 失败:未建立连接 → 通知值带**精确**错误码,不是笼统 -1 */
|
||
static void tc_213_send_error_code_roundtrip(void)
|
||
{
|
||
char buf[4];
|
||
net_msg_t msg;
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM); /* CREATED,未连接 */
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_SEND;
|
||
msg.sockfd = fd;
|
||
msg.buf = buf;
|
||
msg.len = (int)sizeof(buf);
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_NOTCONN,
|
||
"TC-NETSOCK-213a SEND on a not-connected socket: notify value = NET_ERR_NOTCONN (exact code for caller branches)");
|
||
|
||
/* 端到端:公开 net_send 的 BADF 形态(fd 越界由**分派侧**再判一次) */
|
||
reset_all();
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_SEND;
|
||
msg.sockfd = 3; /* 合法范围内但未分配 */
|
||
msg.buf = buf;
|
||
msg.len = 4;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF,
|
||
"TC-NETSOCK-213b in range but unallocated fd: net_get_sock null -> BADF travels on the notify value");
|
||
|
||
/* 调用方上下文恢复 errno:拿到的必须是"本任务的"精确码 */
|
||
reset_all();
|
||
bring_up();
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_send(6, buf, 4, 0); /* 6 未分配 → 分派侧 BADF → 通知值 -2 */
|
||
CHECK(rc == -1, "TC-NETSOCK-213c BSD semantics kept for callers: any failure is -1");
|
||
CHECK(net_get_errno() == NET_ERR_BADF,
|
||
"TC-NETSOCK-213d caller reads the exact code from its own errno slot (E-03 plus notify value together)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-220 RECV 快路径:芯片缓冲里已有数据 → 当场读出,不登记挂起 */
|
||
static void tc_220_recv_fast_path(void)
|
||
{
|
||
char buf[16];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].readable = 1U;
|
||
ch395f_fake_push_recv_len((uint8_t)fd, 1400U);
|
||
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_recv(fd, buf, 10, NET_MSG_DONTWAIT);
|
||
|
||
CHECK(rc == 10, "TC-NETSOCK-220a chip has 1400B but the caller buffer is 10B -> reads 10 (a partial read is legal)");
|
||
CHECK(g_chip.read_calls == 1 && g_chip.last_read_len == 10U,
|
||
"TC-NETSOCK-220b reads only what the caller offered (never the chip available length)");
|
||
CHECK(s_net_socks[fd].recv_bytes == 10U, "TC-NETSOCK-220c received byte statistic accumulates");
|
||
CHECK(s_net_socks[fd].readable == 0U,
|
||
"TC-NETSOCK-220d reading clears readable (the next RECV_OK interrupt sets it again)");
|
||
CHECK(g_peers.pending_reg_calls == 0, "TC-NETSOCK-220e data is already in hand, no pending registration");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-221 RECV 非阻塞无数据 → WOULDBLOCK(且不许登记挂起) */
|
||
static void tc_221_recv_wouldblock(void)
|
||
{
|
||
char buf[8];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
|
||
CHECK(rc == -1 && net_get_errno() == NET_ERR_WOULDBLOCK,
|
||
"TC-NETSOCK-221a DONTWAIT with no data -> -1 plus WOULDBLOCK");
|
||
CHECK(g_peers.pending_reg_calls == 0,
|
||
"TC-NETSOCK-221b non-blocking never registers pending (else the caller thinks it waits while it is parked)");
|
||
|
||
/* 模式属性(net_setblocking(0))与单次标志同判据 */
|
||
CHECK(net_setblocking(fd, 0) == 0, "TC-NETSOCK-221c switching to non-blocking mode succeeds");
|
||
rc = net_recv(fd, buf, (int)sizeof(buf), 0);
|
||
CHECK(rc == -1 && net_get_errno() == NET_ERR_WOULDBLOCK,
|
||
"TC-NETSOCK-221d blocking attribute 0 behaves exactly like NET_MSG_DONTWAIT");
|
||
CHECK(net_recv_nb(fd, buf, (int)sizeof(buf)) == -1 &&
|
||
net_get_errno() == NET_ERR_WOULDBLOCK,
|
||
"TC-NETSOCK-221e net_recv_nb named entry takes the same path");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-222 RECV EOF:会话已结束 + 无数据 → 返 0 并登记重建(不是 -1) */
|
||
static void tc_222_recv_eof_relisten(void)
|
||
{
|
||
char buf[8];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
|
||
|
||
CHECK(rc == 0, "TC-NETSOCK-222a CLOSED with no data -> EOF(0), not an error");
|
||
CHECK(g_peers.listener_req_calls == 1 &&
|
||
g_peers.last_req_reason == (int)NET_LISTENER_REQ_EOF,
|
||
"TC-NETSOCK-222b EOF registers REQ_EOF (the rebuild belongs to the listener layer, this layer never touches the chip)");
|
||
CHECK(g_chip.open_calls == 0 && g_chip.listen_calls == 0 && g_chip.close_calls == 0,
|
||
"TC-NETSOCK-222c the registration path issues NOT ONE chip transaction (single-owner invariant II)");
|
||
|
||
/* 抢先 relisten 后 state 已回 LISTENING,但会话电平仍是"已结束" */
|
||
reset_all();
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
|
||
s_net_socks[fd].session_dead = 1U;
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
|
||
CHECK(rc == 0,
|
||
"TC-NETSOCK-222d still EOF after relisten moved it back to LISTENING (N-32 P1 level, else EOF vanishes forever)");
|
||
|
||
/* auto_relisten=0(客户端 fd):EOF 不登记重建,交调用方决定重连 */
|
||
reset_all();
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_CLOSED;
|
||
s_net_socks[fd].session_dead = 1U;
|
||
s_net_socks[fd].auto_relisten = 0U;
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
rc = net_recv(fd, buf, (int)sizeof(buf), NET_MSG_DONTWAIT);
|
||
CHECK(rc == 0 && g_peers.listener_req_calls == 0,
|
||
"TC-NETSOCK-222e EOF on a non-listener fd must not rebuild behind the caller back");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-223 RECV 阻塞无数据 → 登记挂起 + 跳过即时回执;到点由 tick 结束 */
|
||
static void tc_223_recv_pending_roundtrip(void)
|
||
{
|
||
char buf[16];
|
||
net_msg_t msg;
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_RECV;
|
||
msg.sockfd = fd;
|
||
msg.buf = buf;
|
||
msg.len = (int)sizeof(buf);
|
||
msg.flags = 0;
|
||
msg.timeout_ms = 250U; /* 显式预算必须原样带到登记处 */
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
|
||
CHECK(g_peers.pending_reg_calls == 1 && g_peers.last_pend_fd == fd,
|
||
"TC-NETSOCK-223a blocking with no data -> registers a pending recv (netTask returns with zero occupancy)");
|
||
CHECK(g_peers.last_pend_to == 250U,
|
||
"TC-NETSOCK-223b the caller explicit budget travels with the registration (0 means infinite, here it must not be 0)");
|
||
CHECK(g_peers.last_pend_caller == (TaskHandle_t)&g_ns_task_caller,
|
||
"TC-NETSOCK-223c completion must know whom to notify");
|
||
CHECK(g_ns_notify_calls == 0,
|
||
"TC-NETSOCK-223d registering then immediately notifying would pass off registration as the result - pinned absent");
|
||
|
||
/* 数据到达:由挂起 recv 的 tick 结束(脚本模拟) */
|
||
ns_peers_arm_pending_done(fd, 7);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_calls == 1 && g_ns_notify_last_val == 7U,
|
||
"TC-NETSOCK-223e receipt only once data arrives, notify value = byte count");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-224 net_recv_to:分片超时用独立返回码 -2(不靠 errno,避免跨任务竞态) */
|
||
static void tc_224_recv_to_timeout_code(void)
|
||
{
|
||
char buf[16];
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
|
||
/* 先造一条"已登记"的挂起,再让 tick 判到点 */
|
||
ns_peers_arm_pending_done(fd, NET_ERR_TIMEDOUT);
|
||
rc = net_recv_to(fd, buf, (int)sizeof(buf), 30U);
|
||
CHECK(rc == -2, "TC-NETSOCK-224a this slice timed out -> dedicated return code -2 (distinct from a real error -1)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-225 RECV 参数非法:不碰芯片、不登记 */
|
||
static void tc_225_recv_bad_args(void)
|
||
{
|
||
char buf[4];
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
|
||
CHECK(net_recv_to(0, buf, 4, 0U) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-225a net_recv_to(timeout=0) is refused up front - 0 means infinite wait on the netTask side,"
|
||
"which contradicts this API whole reason for having a bounded occupancy");
|
||
CHECK(g_ns_put_calls == 0, "TC-NETSOCK-225b the refusal happens before enqueue");
|
||
|
||
CHECK(net_recv(fd, NULL, 4, NET_MSG_DONTWAIT) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-225c buf NULL -> INVAL");
|
||
CHECK(net_recv(fd, buf, 0, NET_MSG_DONTWAIT) == -1 &&
|
||
net_get_errno() == NET_ERR_INVAL, "TC-NETSOCK-225d len <= 0 -> INVAL");
|
||
CHECK(net_send(fd, NULL, 4, 0) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-225e send with buf NULL -> INVAL");
|
||
CHECK(net_send(fd, buf, -1, 0) == -1 && net_get_errno() == NET_ERR_INVAL,
|
||
"TC-NETSOCK-225f send with len < 0 -> INVAL");
|
||
CHECK(g_chip.write_calls == 0 && g_chip.read_calls == 0 &&
|
||
g_peers.pending_reg_calls == 0 && g_peers.send_reg_calls == 0,
|
||
"TC-NETSOCK-225g on the invalid-argument path: zero chip transactions and zero pending registrations");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-230 CLOSE:先结束挂起再关芯片,且 last_peer_port 跨释放保留 */
|
||
static void tc_230_close_order_and_peer(void)
|
||
{
|
||
net_msg_t msg;
|
||
int fd;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
/* 登记一条挂起 recv,再看 close 是否先把它结算掉(排空/取消都必通知) */
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_RECV;
|
||
msg.sockfd = fd;
|
||
msg.buf = (void *)"x";
|
||
msg.len = 1;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_net; /* 另一个任务在等这条流 */
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages(); /* 走登记路径(不发回执) */
|
||
CHECK(g_peers.pending_reg_calls == 1 && g_ns_notify_calls == 0,
|
||
"TC-NETSOCK-230a precondition: pending registered and no receipt yet");
|
||
|
||
/* 芯片侧此刻仍是 OPEN:close 才需要真去拆(已 CLOSED 的形态见 TC-NETSOCK-232) */
|
||
g_fake.cached_open[fd] = CH395F_SOCKET_OPEN;
|
||
g_ns_notify_calls = 0;
|
||
{
|
||
s_close_base_clear = g_peers.listener_clear_calls;
|
||
s_close_base_settle = g_peers.pending_settle_calls;
|
||
}
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_CLOSE;
|
||
msg.sockfd = fd;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
|
||
CHECK(g_peers.pending_settle_calls == s_close_base_settle + 1,
|
||
"TC-NETSOCK-230b close must first settle this fd pending recv (else the waiter hangs forever)");
|
||
CHECK(g_peers.pending_notified == 1,
|
||
"TC-NETSOCK-230c settling always notifies (a cancel notifies too, not a silent slot clear)");
|
||
CHECK(g_chip.close_calls >= 1 && g_fxchip.rst_begin_calls == 1,
|
||
"TC-NETSOCK-230d chip still OPEN -> sock_reset deterministically drains and tears it down");
|
||
CHECK(g_fxchip.glob_calls == 0,
|
||
"TC-NETSOCK-230e close must never read the global interrupt register (B3: sole reader is net_poll)");
|
||
CHECK(s_net_socks[fd].in_use == 0U && s_net_socks[fd].last_peer_port == 51999U,
|
||
"TC-NETSOCK-230f control block cleared but last_peer_port kept (ghost discrimination is chip-socket level)");
|
||
CHECK(g_peers.listener_clear_calls == s_close_base_clear + 1,
|
||
"TC-NETSOCK-230g close idempotently clears the listener retry (closing cancels pending retries, D10)");
|
||
CHECK(g_peers.pend_active_now[fd] == 0,
|
||
"TC-NETSOCK-230h after close this fd has no pending recv (both tables zeroed)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-231 CLOSE 未在用:net_get_sock 判空 → BADF 通知值 */
|
||
static void tc_231_close_badfd(void)
|
||
{
|
||
net_msg_t msg;
|
||
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_CLOSE;
|
||
msg.sockfd = 5;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF,
|
||
"TC-NETSOCK-231a close on an unallocated fd -> BADF travels back on the notify value");
|
||
CHECK(g_chip.close_calls == 0, "TC-NETSOCK-231b BADF does not touch the chip");
|
||
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_ACCEPT; /* 没有 in_use 的监听 */
|
||
msg.sockfd = 5;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_INVAL,
|
||
"TC-NETSOCK-231c accept on an unused fd -> INVAL (control-block check, not fd range)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-240 DROP_CONN:四类前置各走各的分支(幂等语义是关键) */
|
||
static void tc_240_drop_conn_branches(void)
|
||
{
|
||
net_msg_t msg;
|
||
int fd;
|
||
|
||
/* 非在用 → BADF */
|
||
bring_up();
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_DROP_CONN;
|
||
msg.sockfd = 2;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_BADF, "TC-NETSOCK-240a unallocated -> BADF");
|
||
|
||
/* UDP → INVAL */
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_DGRAM);
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_DROP_CONN;
|
||
msg.sockfd = fd;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == (uint32_t)NET_ERR_INVAL,
|
||
"TC-NETSOCK-240b not a TCP stream -> INVAL (this API only tears down TCP sessions)");
|
||
|
||
/* 监听态但无活动会话 → 幂等 0,不动芯片 */
|
||
bring_up();
|
||
fd = net_socket(NET_AF_INET, NET_SOCK_STREAM);
|
||
s_net_socks[fd].state = NET_SOCK_STATE_LISTENING;
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_DROP_CONN;
|
||
msg.sockfd = fd;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
CHECK(g_ns_notify_last_val == 0U, "TC-NETSOCK-240c no active session -> idempotent success 0");
|
||
CHECK(g_chip.close_calls == 0 && g_fxchip.rst_begin_calls == 0,
|
||
"TC-NETSOCK-240d the idempotent branch must not tear down the chip (it would destroy the live listener)");
|
||
|
||
/* ESTABLISHED + 监听型 → QUARANTINE 拆除 + 登记重建 */
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
s_net_socks[fd].auto_relisten = 1U;
|
||
(void)memset(&msg, 0, sizeof(msg));
|
||
msg.type = NET_MSG_DROP_CONN;
|
||
msg.sockfd = fd;
|
||
msg.caller = (TaskHandle_t)&g_ns_task_caller;
|
||
(void)ns_rtos_q_put_raw(&msg);
|
||
net_process_messages();
|
||
|
||
CHECK(g_peers.pending_settle_calls == 1 && g_peers.send_cancel_calls == 1,
|
||
"TC-NETSOCK-240e both pending tables are ended before dropping the session (with notification)");
|
||
CHECK(g_peers.listener_settle_calls == 1 &&
|
||
g_peers.last_settle_level == (int)NET_LISTENER_SETTLE_QUARANTINE &&
|
||
g_fxchip.rst_begin_calls == 0,
|
||
"TC-NETSOCK-240f listener teardown is REGISTERED for the tick machine (QUARANTINE settle + REQ_DROP) - the handler starts no reset machine and never waits (N-17)");
|
||
CHECK(g_peers.listener_req_calls == 1 &&
|
||
g_peers.last_req_reason == (int)NET_LISTENER_REQ_DROP,
|
||
"TC-NETSOCK-240g listener-type fd registers REQ_DROP after the drop (rebuild belongs to the listener layer)");
|
||
CHECK(s_net_socks[fd].state == NET_SOCK_STATE_CLOSED &&
|
||
s_net_socks[fd].in_use == 1U && s_net_socks[fd].local_port == 2404U &&
|
||
s_net_socks[fd].auto_relisten == 1U,
|
||
"TC-NETSOCK-240h only session bookkeeping is closed out: fd, listener attribute and port all survive");
|
||
CHECK(g_fxchip.glob_calls == 0,
|
||
"TC-NETSOCK-240i teardown only touches its own socket (F-NET-7: never reads the global register)");
|
||
reset_all();
|
||
}
|
||
|
||
/* TC-NETSOCK-250 入队失败(队列满)→ 调用方 BUSY,不留下"已提交"的假象 */
|
||
static void tc_250_put_fail_busy(void)
|
||
{
|
||
int fd;
|
||
int rc;
|
||
|
||
bring_up();
|
||
fd = mk_established(NET_SOCK_STREAM);
|
||
ns_rtos_q_fill((int)NS_Q_CAP); /* 把队列灌满 */
|
||
g_ns_cur_task = (TaskHandle_t)&g_ns_task_caller;
|
||
|
||
rc = net_send(fd, "abc", 3, 0);
|
||
CHECK(rc == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-250a queue full -> net_send reports BUSY, it does not silently drop");
|
||
CHECK(g_chip.write_calls == 0 && g_ns_notify_calls == 0,
|
||
"TC-NETSOCK-250b on enqueue failure: zero chip transactions and zero notifications (caller will not wait in vain)");
|
||
CHECK(net_listen(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-250c the same shape holds for net_listen");
|
||
CHECK(net_close(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-250d the same shape holds for net_close");
|
||
CHECK(net_drop_conn(fd) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-250e the same shape holds for net_drop_conn");
|
||
CHECK(net_recv_to(fd, (void *)"x", 1, 5U) == -1 && net_get_errno() == NET_ERR_BUSY,
|
||
"TC-NETSOCK-250f the same shape holds for net_recv_to");
|
||
reset_all();
|
||
}
|
||
|
||
static void ns_run_s3(void)
|
||
{
|
||
tc_200_unknown_type();
|
||
tc_201_ticks_run_before_dispatch();
|
||
tc_202_no_queue_bound();
|
||
tc_210_send_end_to_end();
|
||
tc_211_send_chunking();
|
||
tc_212_send_pending_defers_notify();
|
||
tc_213_send_error_code_roundtrip();
|
||
tc_220_recv_fast_path();
|
||
tc_221_recv_wouldblock();
|
||
tc_222_recv_eof_relisten();
|
||
tc_223_recv_pending_roundtrip();
|
||
tc_224_recv_to_timeout_code();
|
||
tc_225_recv_bad_args();
|
||
tc_230_close_order_and_peer();
|
||
tc_231_close_badfd();
|
||
tc_240_drop_conn_branches();
|
||
tc_250_put_fail_busy();
|
||
}
|