Files
STM32F4-Base/test/host/net_socket/ns_s3_msg.c
T
wandering1 b7c0889cc7 refactor(编号·批次二): 代码与测试侧 215 处保护功能号改成功能名,零行为改动
用户裁定「不要用字母加数字的编号,语义标识是最好的」后的批次二代码半(道 ui-param 执行,逐文件站点数与
自证见其交付)。唯一口径 = 映射表 ci-artifacts/lprot_map.tsv 的功能名列原文,**零自造同义词**;L2.5 与
L-TEST 经实测各 0 处(不是本族),TC 族一字未动。

固件 22 文件 128 处(adc_task.c 21、phasor.c 22、phasor.h 13…),主机测试 13 文件 87 处(pha
sor_test.c
57…);设计文档指针新增 19 行,另 16 只文件本来就写着规划文档 ⇒ 不重复加。

**两条只有真改过 215 处才会踩到的坑,都写进注释规范实践**:
· 指针**不能接在原句尾部**——那会替原文一个未闭合的半括号封口,实测咬坏 App/algo/sample_ring.h 这类
  跨行句 ⇒ 指针一律**单独成行**;
· 号删掉会留下**叠词**(「自检失败闭锁 闭锁判定」「定值校验和 定值巡检」)⇒ 复扫并收口 4 处,措辞只用
  映射名本身;具体对象需要留住时写成「自检失败闭锁(采样故障闭锁)」。

自证(不靠嘴说):改前把 35 只文件原文备份到仓外 temp,逐文件比 `/*`、`*/` 计数、三类括号计数、行数增量
恰等于指针行数、全角括号增删相等、残留号 0 ⇒ **35/35 通过**;Lead 复核后跑权威 @build **0 错 0 警**,
Program Size `Code=233900 RO=26104 RW=1536 ZI=136808` —— **与三条道各自改前改后测的四数逐
字节同数**,
这就是「只动注释与文本」的机器证据;八套主机单测 exit 0,cppcheck 按钩子参数零发现,coding-standard 0。

文件头「修改记录」里的号**一并换名、不还原**(Lead 裁定):这些号是全仓退役的,留一个查无此号的引用比
留一条改过名的条目更坏——「冻结历史不回改」保护的是**别替历史改口**,不是保护一个再也查不到的字符串;
id-check 对变更记录行本就豁免,两种选择都不影响门禁。
2026-10-27 08:59:26 +08:00

616 lines
26 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.
/*
* 分片 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 (errno slot 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 (listener lifecycle 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 (heartbeat gap fix)");
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 (sole reader of the global register: never reads it elsewhere)");
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();
}