Files
STM32F4-Base/Drivers/BSP/NET/net_pending.h
T
wandering1 0f7b5a87fb chore(编号): 存量编号全量转语义短语,门禁识别面与语料同批重打
登记簿、文档与代码注释里的存量字母加数字编号(N-x / S-x / T-x / E-x / A-x / 卡号)
一律改写为语义短语,净删 2,028 处引用;删除基线与待确认、并行分道方案、排期详述、
采集保护收尾 4 篇(内容已并入 docs/任务规划/README 与登记簿)。

门禁机器与三张账同批重打:check_ids.py 新增变更记录行豁免与第二根护栏
undecidable_total_ceiling;check_test_traceability.py 只加 stdout/stderr UTF-8
reconfigure(TC_RE 未动);check_cubemx_survival.py 的 --write 固定 newline=LF;
id_registry / id_aliases(+19 条 retired-to-phrase)/ id_known_backlog 重打。

test/ 56 个文件同步 TC 标签语义化,其中 18 个 TU 改了 CHECK / printf 里的标签文字,
是本批唯一改变构建产物的文件集合。

为什么合成一笔、不拆四笔(实测撞闸,非偏好):pre-commit 跑钩子前会 stash 未 staged
部分,拆开后中间提交必然是「新规则 + 旧语料」——实跑可判定 85.15% < 基线 86.00%、
不可判定 1,453 > 上限 1,166,编号门禁直接拦下。编号识别面与它描述的语料只能同码移动。

实跑基线(工作区全量):754 文件 / 8,230 处编号字面量 / 可判定 7,079 = 86.01%
(基线 86.0%,容差 0.1pp)/ 不可判定绝对数 1,151(上限 1,166)/ 校验 1-6 错误级 0 /
告警 = 悬空 2(App/algo/freq.c 的 S2,属 §4.4 同号不同义)+ 孤儿 50。

基线从 84.0 抬到 86.0 的理由见 docs/门禁说明/id-check.md §7.6:本批真删了 2,028 处
引用,可判定面随真实内容上升,不是把分母做小。同批新增排除 docs/硬件平台/ 语料,使
识别面少 264 处——芯片手册位号与网表料号不属编号空间(registry 有 excluded_paths_note)。

⚠ 余量只剩 0.11pp(约 10 处)与绝对数 15 处:把「可判定的引用」改成散文会立刻撞第二
根护栏。另遗留一项:id_aliases 里 retired 的 N / S / T / A 别名没有 scope(U1~U5 有),
今后有人新写一个 A-4,校验 3 不会响,只能靠人,建议下批补 scope。

固件不变证据:115 个 .c/.h 剥注释并归一字符串后与 HEAD 逐字节全等,残留代码差异 0;
App / Src / Drivers 的字符串字面量零改动。@build 0 错误 0 警告;40/40 主机套件、2,889
断言全通过;覆盖率 line 89.2% / func 92.6%;pre-commit 48 钩子 --all-files 全绿;
check_doc_links --all 通过(176 文档,章节引用无法判定 0);逐字节卫生核过:14 个带
BOM 文件与 HEAD 一致、0 个 CR、0 个 U+FFFD,字符串字面量纯 ASCII。

.cubemx_survival_baseline.json 的 watchdog-module 哈希 56bf27 变 2bb051 属合法:
Src/freertos.c 的 USER CODE 区内只有注释文字随编号改写,代码未变。

同批带上体积同码凭证与 RAM 台账行(@build 之后盖戳):RW_IRAM1 79,008 B / 60.3%,
RW_CCM 57,376 B / 87.9%,阈值 92%。
2026-10-26 12:28:14 +08:00

216 lines
12 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.
/*
* 模块名称:挂起请求表(阻塞 recv / send 挂起模型)
* 文件说明:阻塞 recv / send 的"挂起请求"数据结构与生命周期:登记 / 每轮完成检查 /
* 取消(close、网络重建、对账 resync)/ 幂等清理。
* recv 侧 = 数据到达即完成;send 侧 = 分块推进至全部写完(进度刷新停滞计时)。
* 架构与五路语义见 docs/网络/Net层阻塞模型架构设计.md
* 依赖: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
#include <stdint.h>
#include "FreeRTOS.h"
#include "task.h"
#include "net_types.h"
#include "ch395f.h"
/* 内部哨兵:pending 已登记(消息分派据此跳过即时通知;取值不与字节数空间冲突) */
#define NET_PENDING_REGISTERED 0x70000000
/* 挂起超时留痕探针:排查"调用方挂死唤不醒"时置 1——挂起超 NET_RECV_TRIPWIRE_MS
* 只留一条 [ERR](哪个 fd/多少毫秒/什么基准),绝不强制结束、绝不伪造返回值。
* 开与关语义完全一致,差别只在留痕;默认 0 = 产品构建零成本。
* 见 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 顶部调用一次;登记守卫据此拒绝自等待死锁)
* 入口参数:无
* 出口参数:无
* 返回值:无
* 限定条件:执行任务(netTask / 硬件层 = testTask)上下文,创建消息队列前
*/
void net_pending_capture_owner(void);
/*
* 函数功能:登记一个挂起 recv 请求(net_recv_locked 的阻塞无数据路径)
* 入口参数:sockfd - Socket 描述符 (0~7) int
* buf - 调用方接收缓冲(须保持有效至完成) void *
* len - 缓冲容量 int
* to_ms - 本次等待预算(0 = 无限等待) uint32_t
* caller - 调用方任务句柄(完成通知目标) TaskHandle_t
* 出口参数:无
* 返回值:NET_PENDING_REGISTERED - 登记成功(分派跳过即时通知)
* -1 - 拒绝(同 fd 已有挂起 / 执行任务自身发起;错误码经 NET_ERR_RETURN_1)
* 限定条件:netTask 上下文
* 函数说明:每 fd 至多一个挂起(禁止覆盖——覆盖丢失前调用方通知 = 该任务永久挂死);
* 执行任务自身发起的阻塞等待无他人可完成 → 自等自通知 = 死锁,必须拒绝
*/
int net_pending_register(int sockfd, void *buf, int len,
uint32_t to_ms, TaskHandle_t caller);
/*
* 函数功能:挂起 recv 完成检查(netTask 每轮 net_process_messages 调用一次,非阻塞)
* 入口参数:p_socks - Socket 控制块表首指针(net_socket.c 的 s_net_socks) net_sock_t *
* 出口参数:无
* 返回值:无
* 限定条件:netTask 上下文
* 函数说明:逐 fd 按序判定:①数据到达 ②到点(仅显式预算) ③断连(排空→数据或
* EOF/CONNRESET);命中即经公共出口通知并清槽;另有编译期留痕探针
* (NET_PENDING_TRIPWIRE,默认关)
*/
void net_pending_tick(net_sock_t *p_socks);
/*
* 函数功能:清除本 fd 的挂起请求(不通知;"新 fd 不得继承"与 close 后的幂等清理)
* 入口参数:sockfd - Socket 描述符 (0~7) int
* 出口参数:无
* 返回值:无
* 限定条件:无
*/
void net_pending_clear(int sockfd);
/*
* 函数功能:取消本 fd 的挂起请求并通知调用方(显式路径:对账 resync 等)
* 入口参数:sockfd - Socket 描述符 (0~7) int
* err - 通知的错误码(如 NET_ERR_CONNRESET) int
* 出口参数:无
* 返回值:无(无在位挂起时静默返回)
* 限定条件:netTask 上下文
*/
void net_pending_cancel(int sockfd, int err);
/*
* 函数功能:取消全部挂起请求并逐一通知(网络重建路径)
* 入口参数:err - 通知的错误码 int
* 出口参数:无
* 返回值:无
* 限定条件:netTask 上下文;须在控制块清零与芯片复位之前调用
*/
void net_pending_cancel_all(int err);
/*
* 函数功能:关闭前先结束本 fd 的挂起 recv——有数据先送达(按字节数),无数据则
* 取消(CONNRESET),然后调用方才执行 close/拆会话(per-fd 串行)
* 入口参数:sockfd - 将被关闭的描述符 (0~7) int
* p_sock - 将被关闭的 Socket 控制块指针(与 sockfd 对应) net_sock_t *
* 出口参数:无
* 返回值:无(无在位挂起时静默返回)
* 限定条件:netTask 上下文;须在 ch395f_close_socket 之前调用
*/
void net_pending_settle_close(int sockfd, net_sock_t *p_sock);
/*
* 函数功能:查询本 fd 是否有在途挂起 recv —— 宿主能力,供 net_listener 消费
* 入口参数:sockfd - Socket 描述符 (0~7) int
* 出口参数:无
* 返回值:1 - 有挂起;0 - 无(含描述符越界) int
* 限定条件:任意上下文(纯 RAM 读,不发起 RPC)
* 函数说明:监听生命周期模块化:net_listener_tick 用它跳过"重开 Socket
* 会摧毁在途连接"的 fd(挂起 recv 保险)
*/
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 末块等待即此情形,
* 见《Net层阻塞模型架构设计.md》 §5.5 末块边界)。
*/
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_tick 据此跳过
* "重开 Socket 会摧毁在途传输"的 fd(挂起 send 保险)
*/
int net_send_pending_active(int sockfd);
#endif /* NET_PENDING_H */