feat(NET): SEND pending 二期落地 + 末块挂起越界缺陷修复(台架复测闭环)
net_pending.c/h 增设第二张挂起表 s_net_send[](与 recv 表完全独立,收发 互不阻塞);net_send_locked 快速路径改为有界自旋,单次等待超出 NET_SEND_SPIN_MS(2ms) 即登记挂起 send,由 net_send_pending_tick 按主循环 节拍分块推进,netTask 最坏停滞由 1s 压到 2ms。net_listener_retry_tick 的 挂起保险扩展到 send 侧;net_close_locked / 网络重建 / 状态对账三路均先 取消再动作(取消必通知,调用方永不挂死)。 台架复测(A/B 对照 + 插桩定位)发现并修复末块挂起越界缺陷:内层"等本块 发完(SENBUF_FREE)"位于写块之后,写完末块时 sent 已 == len,若该次等待越过 2ms 预算便会调用 net_send_pending_register,而登记函数按 sent >= len 拒收 并回 -1,把一次实际已完整交付芯片的发送报成失败。修复为内层等待开头判 sent >= len 即跳出(末块已交付、SENBUF_FREE 等待是纯保守自旋),并置于 超时/自旋判定之前;该守卫同时保证后续登记必伴随 sent < len。 复验:ftp_regression 连续 19 次 11/11;ftp_integrity_test 20 轮 x 64KB STOR/RETR SHA-256 20/20 一致(两轮),吞吐稳定约 80KB/s;设备日志零 write error;ST-LINK 回读 No difference found。 文档:Net层阻塞模型架构设计.md 补二期设计与新增 9.1 缺陷记录,netTask 栈实测峰值按本次台架 920B 订正为 1116/3840(FTP 传输期 1164);网络自愈 架构设计.md 吸收原监听生命周期文档、新增编号术语导读表,并把 5.2 "实验锚点速查"的 E 编号含义与 E-02 审计编号歧义写在表头(用户反馈该表 E 编号无法读懂)。 门禁:Keil 0 错 0 警;cppcheck / 编码规范 / 乱码零告警; check_mem_numbers PASS;check_doc_links --all --sections-strict 142 篇 PASS。
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
/*
|
||||
* 模块名称:挂起 recv 请求表(阻塞 recv 挂起模型)
|
||||
* 文件说明:阻塞 recv 的"挂起请求"数据结构与生命周期:登记 / 每轮完成检查 /
|
||||
* 模块名称:挂起请求表(阻塞 recv / send 挂起模型)
|
||||
* 文件说明:阻塞 recv / send 的"挂起请求"数据结构与生命周期:登记 / 每轮完成检查 /
|
||||
* 取消(close、网络重建、对账 resync)/ 幂等清理。
|
||||
* recv 侧 = 数据到达即完成;send 侧 = 分块推进至全部写完(进度刷新停滞计时)。
|
||||
* 架构与五路语义见 docs/网络/Net层阻塞模型架构设计.md
|
||||
* 依赖:net_types.h(net_sock_t);消费方 = net_socket.c(消息分派 / close /
|
||||
* 重建 / 对账)
|
||||
* 限定条件:除 net_pending_active / net_pending_clear(纯 RAM 读)外,
|
||||
* 全部函数须 netTask 上下文
|
||||
* 依赖:net_types.h(net_sock_t);ch395f.h(send 侧分块写 + 缓冲常量);
|
||||
* 消费方 = net_socket.c(消息分派 / close / 重建 / 对账)、
|
||||
* net_listener.c(挂起保险查询)
|
||||
* 限定条件:除 net_pending_active / net_send_pending_active / *_clear(纯 RAM 读)
|
||||
* 外,全部函数须 netTask 上下文
|
||||
*/
|
||||
#ifndef NET_PENDING_H
|
||||
#define NET_PENDING_H
|
||||
@@ -15,6 +17,7 @@
|
||||
#include "FreeRTOS.h"
|
||||
#include "task.h"
|
||||
#include "net_types.h"
|
||||
#include "ch395f.h"
|
||||
|
||||
/* 内部哨兵:pending 已登记(消息分派据此跳过即时通知;取值不与字节数空间冲突) */
|
||||
#define NET_PENDING_REGISTERED 0x70000000
|
||||
@@ -22,12 +25,21 @@
|
||||
/* 挂起超时留痕探针:排查"调用方挂死唤不醒"时置 1——挂起超 NET_RECV_TRIPWIRE_MS
|
||||
* 只留一条 [ERR](哪个 fd/多少毫秒/什么基准),绝不强制结束、绝不伪造返回值。
|
||||
* 开与关语义完全一致,差别只在留痕;默认 0 = 产品构建零成本。
|
||||
* 见 docs/网络/Net层阻塞模型架构设计.md §9 */
|
||||
* 见 docs/网络/Net层阻塞模型架构设计.md §12 */
|
||||
#define NET_PENDING_TRIPWIRE 0
|
||||
#if NET_PENDING_TRIPWIRE
|
||||
#define NET_RECV_TRIPWIRE_MS 60000U /* 挂起超时留痕阈值(ms) */
|
||||
#endif
|
||||
|
||||
/* 单次写入 CH395F 发送 FIFO 的最大字节数:受硬件发送缓冲
|
||||
* (CH395F_SEND_BUF_SIZE_BYTES = 1KB)与 SPI DMA 载荷
|
||||
* (CH395F_SPI_DMA_MAX_PAYLOAD = 2KB)双重限制,以硬件发送缓冲为硬上限
|
||||
* (TRAP-NET-16:单次写 2KB/4KB 会写穿 1KB 发送 FIFO)。
|
||||
* ⚠ 唯一定义处:发送快速路径(net_send_locked)与挂起 send 推进
|
||||
* (net_send_pending_tick)**必须**共用同一粒度,否则两条路径的分块行为漂移。 */
|
||||
#define NET_SEND_CHUNK_MAX ((int)CH395F_SEND_BUF_SIZE_BYTES < (int)CH395F_SPI_DMA_MAX_PAYLOAD ? \
|
||||
(int)CH395F_SEND_BUF_SIZE_BYTES : (int)CH395F_SPI_DMA_MAX_PAYLOAD)
|
||||
|
||||
/*
|
||||
* 函数功能:捕获执行任务句柄(net_init 顶部调用一次;登记守卫据此拒绝自等待死锁)
|
||||
* 入口参数:无
|
||||
@@ -116,4 +128,88 @@ void net_pending_settle_close(int sockfd, net_sock_t *p_sock);
|
||||
*/
|
||||
int net_pending_active(int sockfd);
|
||||
|
||||
/* ================= 挂起 send 请求表(SEND pending,二期) =================
|
||||
* 与 recv 表**完全独立**(各自一张表、各占一槽):收发两个方向互不阻塞,
|
||||
* 同一 fd 可同时有一个挂起 recv 与一个挂起 send(今天的分片实现本就支持该并发)。
|
||||
* 语义差异:recv 是"等一次到达即完成",send 是"分块推进直到全部写完",
|
||||
* 故 send 的停滞计时按**无进展**刷新(大文件传输总时长必然超过单块预算)。
|
||||
*/
|
||||
|
||||
/*
|
||||
* 函数功能:登记一个挂起 send 请求(net_send_locked 的"等发送缓冲"超预算路径)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* buf - 调用方发送缓冲基址(须保持有效至完成;本函数不拷贝数据) const void *
|
||||
* len - 本次发送**总**长度(含登记前已写入芯片的部分) int
|
||||
* sent - 登记前已写入芯片的字节数(进度起点,0 ≤ sent < len) int
|
||||
* caller - 调用方任务句柄(完成通知目标) TaskHandle_t
|
||||
* 出口参数:无
|
||||
* 返回值:NET_PENDING_REGISTERED - 登记成功(分派跳过即时通知)
|
||||
* -1 - 拒绝(参数非法 / 同 fd 已有挂起 send / 执行任务自身发起)
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:每 fd 至多一个挂起 send(禁止覆盖——覆盖丢失前调用方通知 = 永久挂死);
|
||||
* 执行任务自身发起(net_send_sock 阻塞用法)无他人可完成 → 拒绝(自等死锁)。
|
||||
* ⚠ **调用方铁律**:`sent < len` 是调用侧必须保证的**前置条件**,不是本函数的
|
||||
* 兜底。`sent == len` 表示"全部字节已交付芯片、无剩余块可挂起"——此时超预算
|
||||
* 应**直接返回 sent**,不得调用本函数;本函数会按参数非法拒绝并回 -1,
|
||||
* 把一次完整发送报成失败(net_send_locked 末块等待 2026-10-14 台架实证,
|
||||
* 见《Net层阻塞模型架构设计.md》 §9.1)。
|
||||
*/
|
||||
int net_send_pending_register(int sockfd, const void *buf, int len, int sent,
|
||||
TaskHandle_t caller);
|
||||
|
||||
/*
|
||||
* 函数功能:挂起 send 推进与完成检查(netTask 每轮 net_process_messages 调用一次,非阻塞)
|
||||
* 入口参数:p_socks - Socket 控制块表首指针(net_socket.c 的 s_net_socks) net_sock_t *
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:逐 fd 按序判定:①断连(state 非 ESTABLISHED / session_dead)→ 以已发
|
||||
* 字节数结束;②推进——发送缓冲空闲则写下一块(每轮至多一块,写完即清
|
||||
* send_ready,下一块等 SENBUF_FREE 由 net_poll 重新置位);③全部写完 →
|
||||
* 以总长度结束;④无进展超时 → 以已发字节数结束。结果均为**已发字节数**
|
||||
* (短写由调用方按 `ret != len` 判定,与分片实现契约一致)。
|
||||
*/
|
||||
void net_send_pending_tick(net_sock_t *p_socks);
|
||||
|
||||
/*
|
||||
* 函数功能:清除本 fd 的挂起 send(不通知;"新 fd 不得继承"与 close 后的幂等清理)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:无
|
||||
*/
|
||||
void net_send_pending_clear(int sockfd);
|
||||
|
||||
/*
|
||||
* 函数功能:取消本 fd 的挂起 send 并通知调用方(close / drop_conn / 对账 resync / 重建)
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* err - 取消原因(记入 netTask 错误码槽位 + 日志) int
|
||||
* 出口参数:无
|
||||
* 返回值:无(无在位挂起时静默返回)
|
||||
* 限定条件:netTask 上下文
|
||||
* 函数说明:以**已发字节数**结束(已写入芯片的字节不回收——对端可能已收到;
|
||||
* 未发部分放弃,由调用方按短写处理),原因记 errno 与 [ERR] 日志
|
||||
*/
|
||||
void net_send_pending_cancel(int sockfd, int err);
|
||||
|
||||
/*
|
||||
* 函数功能:取消全部挂起 send 并逐一通知(网络重建路径)
|
||||
* 入口参数:err - 取消原因 int
|
||||
* 出口参数:无
|
||||
* 返回值:无
|
||||
* 限定条件:netTask 上下文;须在控制块清零与芯片复位之前调用
|
||||
*/
|
||||
void net_send_pending_cancel_all(int err);
|
||||
|
||||
/*
|
||||
* 函数功能:查询本 fd 是否有在途挂起 send —— 宿主能力,供 net_listener 用
|
||||
* 入口参数:sockfd - Socket 描述符 (0~7) int
|
||||
* 出口参数:无
|
||||
* 返回值:1 - 有挂起;0 - 无(含描述符越界) int
|
||||
* 限定条件:任意上下文(纯 RAM 读,不发起 RPC)
|
||||
* 函数说明:与 net_pending_active 同用途——net_listener_retry_tick 据此跳过
|
||||
* "重开 Socket 会摧毁在途传输"的 fd(挂起 send 保险)
|
||||
*/
|
||||
int net_send_pending_active(int sockfd);
|
||||
|
||||
#endif /* NET_PENDING_H */
|
||||
|
||||
Reference in New Issue
Block a user