Skip to content

[Environment Adaptation] Support Windows DataLoader multiprocess (num_workers > 0)#79417

Open
PlumBlossomMaid wants to merge 32 commits into
PaddlePaddle:developfrom
PlumBlossomMaid:windows_dataloader_multiprocess
Open

[Environment Adaptation] Support Windows DataLoader multiprocess (num_workers > 0)#79417
PlumBlossomMaid wants to merge 32 commits into
PaddlePaddle:developfrom
PlumBlossomMaid:windows_dataloader_multiprocess

Conversation

@PlumBlossomMaid

Copy link
Copy Markdown
Contributor

PR Category

Environment Adaptation

PR Types

New features

Description

背景

Windows 平台上 DataLoader 的 num_workers > 0 长期不支持。当用户在 Windows 上设置 num_workers > 0 时,框架会发出警告并将 num_workers 强制设为 0,退化为单进程模式。对于数据增强复杂、IO 密集的场景,这会导致严重的数据加载性能瓶颈。

改动内容

本 PR 通过 3 层修复实现了 Windows 上的多进程 DataLoader:

1. Windows 共享内存生命周期修复(核心 bug)

Windows 上 UnmapViewOfFile 会在最后一个引用释放时销毁 named file mapping section。当 worker 的 tensor 被 GC 后,RefcountedMemoryMapAllocation::close() 调用 UnmapViewOfFile 销毁了 section,而此时 reader(主进程)可能还没通过 Queue 拿到 IPC 名称来调用 _new_shared_filename。reader 随后打开了一个空 section,读到零值数据。

Linux 上无此问题:munmap 不会销毁 /dev/shm 中的共享内存文件(只有 shm_unlink 才删除名字,且数据保留至所有映射解除)。

修复方法:

  • AllocateMemoryMap(Windows):不关闭 CreateFileMapping 返回的 HANDLE,始终返回给 caller
  • RefcountedMemoryMapAllocation::close()(Windows):只在 info->refcount == 0 时才调用 UnmapViewOfFile。refcount > 0 时保持 view 映射,让 section 存活

2. ParentWatchDog 误判修复

Windows spawn 模式下,os.getppid() 可能返回临时引导进程的 PID 而非真正的父进程 PID。这导致 worker 中的 ParentWatchDog 误判父进程已死,worker 提前正常退出(exit code 0)。

修复方法:os.getppid() 比对替换为 Windows API OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION) + GetExitCodeProcess,直接查询父进程是否存活。

3. CUDA 初始化竞争缓解

Windows spawn 模式下,每个 worker 独立加载 Paddle 并初始化 CUDA。当 12+ 个 worker 同时启动时,NVIDIA 驱动初始化可能触发 STATUS_STACK_BUFFER_OVERRUN (0xC0000409) 崩溃(由 /GS 安全检查检测,通过 __fastfail 直接杀进程,用户态异常处理器无法捕获)。

缓解措施: Windows 上 num_workers > 4 时,worker 启动间隔 0.05s(stagger),错开 CUDA 初始化窗口。

其他改动:

  • 添加 SetUnhandledExceptionFilter crash 日志记录器(Windows),用于 worker 崩溃诊断
  • 移除 mmap_allocatordata_loader 等文件中的 #ifndef _WIN32 守卫
  • 为 Windows 上新增编译的类和函数添加 PADDLE_API 导出标记
  • 添加模块级 pybind11 函数包装(_share_filename 等),解决 MSVC 上类方法注册问题
  • 更新 cmake 文件,无条件编译 data_loader 库
  • 新增 CI 测试:test_dataloader_multiprocess.py(5 个测试用例)

修改文件清单

C++(6 个文件):

  • paddle/phi/core/memory/allocation/mmap_allocator.{h,cc} — Windows 共享内存实现和生命周期修复
  • paddle/fluid/imperative/data_loader.{h,cc} — Windows 进程监控(OpenProcess + GetExitCodeProcess + WaitForSingleObject)
  • paddle/fluid/pybind/imperative.cc — 无条件的绑定注册
  • paddle/fluid/pybind/tensor.cc — 模块级 pybind 包装(Windows 兼容性)

CMake(3 个文件):

  • paddle/phi/core/memory/allocation/CMakeLists.txt — 无条件编译 mmap_allocator
  • paddle/fluid/imperative/CMakeLists.txt — 无条件编译 data_loader
  • paddle/fluid/pybind/CMakeLists.txt — 无条件链接 data_loader

Python(5 个文件):

  • python/paddle/io/reader.py — 移除 Windows 限制,保留 macOS 限制
  • python/paddle/io/dataloader/dataloader_iter.py — Worker 启动 stagger
  • python/paddle/io/dataloader/worker.py — ParentWatchDog 修复、faulthandler、ForkingPickler 注册
  • python/paddle/io/multiprocess_utils.py — 移除平台守卫
  • python/paddle/incubate/multiprocessing/reductions.py — Windows 使用模块级函数

测试(1 个文件):

  • test/legacy_test/test_dataloader_multiprocess.py — 5 个多进程 DataLoader 测试用例

是否引起精度变化

PaddlePaddle-bot

This comment was marked as outdated.

@PlumBlossomMaid PlumBlossomMaid changed the title Support Windows DataLoader multiprocess (num_workers > 0) [Environment Adaptation] Support Windows DataLoader multiprocess (num_workers > 0) Jul 4, 2026

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已完成本轮复查。当前仍有需要阻塞合入的问题,主要是最新 head 中 mmap_allocator 的函数签名/调用不一致会直接导致编译失败,以及 DataLoader worker 外层异常被吞后可能以 0 退出。详细建议已放在行级评论中。

另外,已有其它 review 线程覆盖了 Windows shared memory 生命周期、HANDLE 宽度、getpid() 和 ParentWatchDog 父进程 PID 等阻塞点,建议一起修复后再重新触发 CI。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

};

void AllocateMemoryMap(std::string filename,
void AllocateMemoryMap(std::string *filename,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 优先级:P0

这里把 AllocateMemoryMap 改成接收 std::string *,但函数体仍按对象使用 filename.c_str(),并在冲突重试时执行 filename = GetIPCName()std::string* 没有这些接口,也不能从 std::string 赋值,所以当前 head 会直接编译失败。请把声明/定义恢复为引用,并把调用点从取地址改回直接传入,或者把函数体所有访问都改成正确的指针解引用;前者更符合这里需要回写文件名的语义。

// mmap_allocator.h / .cc
void AllocateMemoryMap(std::string &filename,
                       int *shared_fd,
                       int flags,
                       size_t size,
                       void **base_ptr_);

// AllocateRefcountedMemoryMapAllocation
AllocateMemoryMap(filename, &fd, flags, size + mmap_alignment, &base_ptr);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 优先级:P0

更新:这一处在新提交里只修了部分 filename->... 访问,但签名/类型问题仍然会阻塞构建。当前 .h 声明的是 AllocateMemoryMap(std::string *, intptr_t *).cc 定义仍是 int *shared_fdAllocateRefcountedMemoryMapAllocation 也在头文件声明为 intptr_t shared_fd、实现为 int shared_fd。调用方会按头文件生成 intptr_t 版本符号,当前实现没有对应定义,链接会失败。另外内部重新声明了局部 int fd,外层传给 RefcountedMemoryMapAllocationfd 没有被更新,Windows HANDLE 仍会丢失。

请把声明、定义、构造函数和 pybind 元数据类型一次性统一到 intptr_t(或专门的 Windows HANDLE 封装),并避免在 if 块内 shadow 外层 fd

intptr_t fd = shared_fd;
...
AllocateMemoryMap(&filename, &fd, flags, size + mmap_alignment, &base_ptr);
...
return std::make_shared<RefcountedMemoryMapAllocation>(
    aligned_base_ptr, size, filename, fd, flags, buffer_id);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 优先级:P0

更新:最新提交修掉了 shared_fd 的部分声明/定义不一致,但这个线程里的构建阻塞点仍未完全消除。当前 AllocateMemoryMap 已改成 std::string *filename,Windows 分支冲突重试处仍是 filename = GetIPCName(),POSIX 分支也还在用 filename.c_str() / shm_unlink(filename.c_str())std::string* 没有这些成员,当前代码仍会直接编译失败。

另外,AllocateRefcountedMemoryMapAllocation 现在内部使用 intptr_t fd,但 RefcountedMemoryMapAllocation 的声明和定义仍接收 int fd,Windows HANDLE 进入对象前还是会被窄化。请把文件名访问和 fd/HANDLE 类型一次性统一,例如保留指针签名时至少需要按下面的形态修正:

// collision retry
*filename = GetIPCName();

// POSIX branch
fd = shm_open(filename->c_str(), file_flags, static_cast<mode_t>(0600));
...
shm_unlink(filename->c_str());

// constructor declaration/definition
RefcountedMemoryMapAllocation(void* ptr,
                              size_t size,
                              std::string ipc_name,
                              intptr_t fd,
                              int flags,
                              int buffer_id = -1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 优先级:P0

更新到当前 head 后,前面提到的 std::string* 访问和 int 构造参数窄化已经修掉,但这条 mmap 分配链路仍然会阻塞构建/共享内存路径。

当前 mmap_allocator.h:84-88 声明是 (int flags, intptr_t fd, ...),而 mmap_allocator.cc:262-267 定义是 (intptr_t fd, int flags, ...),Linux CI 报 no declaration matches,Windows CI 也在 mmap_allocator.cc.obj 编译时报 C2511。同时 AllocateRefcountedMemoryMapAllocation 里的 intptr_t fd = 0 会把正常传入的 shared_fd == -1 丢掉,POSIX 分支会跳过 shm_open 并对 fd 0 执行 ftruncate/mmap,使 DataLoader 共享内存路径回归。

请把构造函数声明、定义和 std::make_shared<RefcountedMemoryMapAllocation>(...) 的实参顺序统一,并恢复 fd 初始值,例如:

intptr_t fd = shared_fd;

RefcountedMemoryMapAllocation(void* ptr,
                              size_t size,
                              std::string ipc_name,
                              intptr_t fd,
                              int flags,
                              int buffer_id = -1);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

更新:最新提交已修复构造函数参数顺序和 fd = shared_fd,但同一条 Windows HANDLE 宽度链路还没有完全收口。当前 MemoryMapAllocation::fd_ 已是 intptr_t,但 mmap_allocator.h:66shared_fd() 仍返回 int;随后新增的模块级 _share_filename 会把这个值放进 Python metadata,_new_shared_filename 又用 t[1].cast<int>() 读回。64 位 Windows 上启用 FLAGS_dataloader_use_file_descriptor=True 时,CreateFileMappingA 返回的 HANDLE 仍可能在 metadata 边界被截断,导致 reader 侧拿到无效句柄或关闭错误句柄。

请把共享句柄的 metadata 类型也保持为指针宽度,或在 Windows 上明确禁用 file-descriptor 模式。修复形态建议如下:

// mmap_allocator.h
inline intptr_t shared_fd() const { return fd_; }

// tensor.cc: 类方法和新增的模块级 wrapper 都保持一致
intptr_t shared_fd = -1;
const intptr_t shared_fd = t[1].cast<intptr_t>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

更新:当前提交已经把 MemoryMapAllocation::shared_fd() 改成 intptr_t,这一部分是正确推进;但 metadata 边界还没有完全收口。paddle/fluid/pybind/tensor.cc 里类方法和模块级 wrapper 的 _new_shared_filename 仍然用 const int shared_fd = t[1].cast<int>(),并且新建共享 allocation 时的临时 shared_fd 仍声明为 int。这样 Python tuple 里的 64 位 Windows HANDLE 读回 C++ 时仍可能被截断。

请把这两个 pybind 路径同步改为指针宽度,例如:

intptr_t shared_fd = -1;
...
const intptr_t shared_fd = t[1].cast<intptr_t>();

Comment on lines +444 to +448
# Write to stderr (visible in parent console)
sys.stderr.write(
f"[WORKER pid={os.getpid()}] UNCAUGHT EXCEPTION\n{traceback.format_exc()}\n"
)
sys.stderr.flush()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这个最外层 except 现在只写 stderr 后继续走 finally 并返回,worker 进程会以 0 退出。这样 _worker_loop 初始化阶段或队列循环外抛出的异常不会被 ThrowErrorIfLoadProcessFailed 识别为 worker failure,主进程可能只看到 DataLoader 卡住/提前结束,尤其 Windows 分支只按非零 exit code 报错。请保留诊断日志后重新抛出,或者显式以非零状态退出,让父进程能可靠感知失败。

Suggested change
# Write to stderr (visible in parent console)
sys.stderr.write(
f"[WORKER pid={os.getpid()}] UNCAUGHT EXCEPTION\n{traceback.format_exc()}\n"
)
sys.stderr.flush()
# Write to stderr (visible in parent console)
sys.stderr.write(
f"[WORKER pid={os.getpid()}] UNCAUGHT EXCEPTION\n{traceback.format_exc()}\n"
)
sys.stderr.flush()
raise

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查新提交。部分 bot 线程里的 Windows shared memory 问题已有推进,但仍有会阻塞合入的问题:既有 Nyanpasu 线程补充了 mmap_allocator 签名/句柄类型不一致的新证据,另外新的行级评论覆盖了非 Windows PID helper 递归、_worker_loop 调用点兼容性、以及 Windows ctypes 句柄宽度问题。

上轮指出的 worker 外层异常被吞问题仍在既有线程中覆盖,本次不重复开新线程。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

#ifdef _WIN32
static inline int GetPid() { return static_cast<int>(GetCurrentProcessId()); }
#else
static inline int GetPid() { return GetPid(); }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里的非 Windows 分支会递归调用 GetPid() 自身。当前 GetIPCName()MemoryMapFdSet 日志都会走这个 helper,Linux DataLoader 共享内存路径一进入 GetIPCName() 就可能栈溢出,而不是生成 IPC 名称。请改回调用 POSIX getpid()(上面已经在非 Windows 分支 include 了 <unistd.h>)。

Suggested change
static inline int GetPid() { return GetPid(); }
static inline int GetPid() { return getpid(); }

Comment thread python/paddle/io/dataloader/worker.py Outdated
Comment on lines +86 to +99
handle = ctypes.windll.kernel32.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION, False, self._parent_pid
)
if not handle:
# Fallback: try PROCESS_QUERY_INFORMATION (might fail on some systems)
PROCESS_QUERY_INFORMATION = 0x0400
handle = ctypes.windll.kernel32.OpenProcess(
PROCESS_QUERY_INFORMATION, False, self._parent_pid
)
if not handle:
return True # Can't check, assume alive
exit_code = wintypes.DWORD(0)
ctypes.windll.kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code))
ctypes.windll.kernel32.CloseHandle(handle)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

ctypes.windll.kernel32.OpenProcess 默认 restypec_int,64 位 Windows 上会把 HANDLE 截断成 32 位;随后 GetExitCodeProcess/CloseHandle 可能拿到无效句柄,ParentWatchDog 会误判父进程已退出或泄漏/关闭错误句柄。请给这些 WinAPI 设置 argtypes/restype,用 wintypes.HANDLE 保存句柄后再查询退出码。

Suggested change
handle = ctypes.windll.kernel32.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION, False, self._parent_pid
)
if not handle:
# Fallback: try PROCESS_QUERY_INFORMATION (might fail on some systems)
PROCESS_QUERY_INFORMATION = 0x0400
handle = ctypes.windll.kernel32.OpenProcess(
PROCESS_QUERY_INFORMATION, False, self._parent_pid
)
if not handle:
return True # Can't check, assume alive
exit_code = wintypes.DWORD(0)
ctypes.windll.kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code))
ctypes.windll.kernel32.CloseHandle(handle)
kernel32 = ctypes.windll.kernel32
kernel32.OpenProcess.argtypes = [
wintypes.DWORD,
wintypes.BOOL,
wintypes.DWORD,
]
kernel32.OpenProcess.restype = wintypes.HANDLE
kernel32.GetExitCodeProcess.argtypes = [
wintypes.HANDLE,
ctypes.POINTER(wintypes.DWORD),
]
kernel32.GetExitCodeProcess.restype = wintypes.BOOL
kernel32.CloseHandle.argtypes = [wintypes.HANDLE]
kernel32.CloseHandle.restype = wintypes.BOOL
handle = kernel32.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION,
False,
wintypes.DWORD(self._parent_pid),
)
if not handle:
# Fallback: try PROCESS_QUERY_INFORMATION (might fail on some systems)
PROCESS_QUERY_INFORMATION = 0x0400
handle = kernel32.OpenProcess(
PROCESS_QUERY_INFORMATION,
False,
wintypes.DWORD(self._parent_pid),
)
if not handle:
return True # Can't check, assume alive
exit_code = wintypes.DWORD(0)
try:
if not kernel32.GetExitCodeProcess(handle, ctypes.byref(exit_code)):
return True
finally:
kernel32.CloseHandle(handle)

Comment thread python/paddle/io/dataloader/worker.py Outdated


def _worker_loop(
parent_pid,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里把 parent_pid 插到 _worker_loop 的第一个必选位置,但仓库里 test/legacy_test/test_multiprocess_dataloader_exception.py 仍有两处直接按旧签名调用 _worker_loop(...)。这些调用现在会把 dataset 当成 parent_pid,并因为缺少 base_seed 直接 TypeError/参数错位,导致既有 DataLoader 异常测试失败。请同步更新所有直接调用,或把新参数放到末尾并保留默认值,避免破坏旧的内部调用形态。

def _worker_loop(
    dataset,
    dataset_kind,
    indices_queue,
    out_queue,
    done_event,
    auto_collate_batch,
    collate_fn,
    drop_last,
    init_fn,
    worker_id,
    num_workers,
    use_shared_memory,
    base_seed,
    shm_cache_size=0,
    parent_pid=None,
):
    ...
    parent_watch_dog = ParentWatchDog(parent_pid)

# dataloader_iter.py 中再把 os.getpid() 作为最后一个参数传入。

PaddlePaddle-bot

This comment was marked as outdated.

…ch, worker parent_pid, except raise, ctypes HANDLE

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。worker 父进程 PID 传参、外层异常重新抛出、GetPid() 递归和 WinAPI HANDLE 的 ctypes 声明等前序问题看起来已经修复。

不过 mmap_allocator 这一条链路仍有会阻塞构建/Windows 句柄正确性的残留问题,我已在既有线程补充了当前 head 的具体证据。请先处理该线程后再合入。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

PaddlePaddle-bot

This comment was marked as outdated.

PaddlePaddle-bot

This comment was marked as outdated.

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。前序 std::string* 访问、HANDLE 构造参数窄化、worker/PID/ctypes 相关问题看起来已有修复,Pre Commit 也已通过。

mmap_allocator 仍有阻塞构建/共享内存路径的问题,我已在既有 Nyanpasu 线程补充当前 head 和 CI 日志证据。请先处理该线程后再合入。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。mmap_allocator 的构造函数参数顺序和 fd 初始值问题已经修复,前序 worker/PID/ctypes 相关问题也仍看起来已处理。

不过 Windows 共享句柄在 Python metadata 边界仍会从 intptr_t 收窄到 int,我已在既有 Nyanpasu 线程补充当前 head 的证据和修复方向。该问题会影响 file-descriptor 模式下 64 位 Windows 句柄正确性,建议修复后再合入。

当前仍需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

@PaddlePaddle-bot

PaddlePaddle-bot commented Jul 5, 2026

Copy link
Copy Markdown

🤖 Paddle-CI-Agent | ci_status_monitor | 2026-07-17 08:25:06 UTC+08:00

CI报告基于以下代码生成(30分钟更新一次):
PR commit: 56566af | Merge base: 18c11b8 (branch: develop)


1 Required任务 : 37/48 通过

总执行(rerun次数) 总任务 ✅ 通过 ❌ 失败 ⏳ 运行中 ⏸️ 等待中 跳过
80(0) 80 69 5 0 0 6
任务 错误类型 置信度 日志
Coverage build 环境问题:Runner 通信中断 Job
Coverage build 环境问题:覆盖率基线下载超时 Job
Linux-DCU / Test PR问题:DataLoader worker 段错误 Job
PR-CI-Inference / Build 环境问题:Runner 通信中断 Job
Check approval 需要 Approval Job

2 失败详情

🔴 Coverage build — 环境问题(置信度: 高)

分析器: 通用分析(fallback)

失败用例:

用例 错误摘要
Build self-hosted runner 与 GitHub 失联,构建步骤未正常结束

关键日志:

The self-hosted runner lost communication with the server.
Build: status=in_progress
Clean up env: status=pending
Upload coverage product: status=pending
  • 根因摘要: Runner 通信中断导致 Coverage 构建未完成

深度状态显示 Build 仍为 in_progress,后续清理和产物上传均未执行;快速摘要明确为 self-hosted runner lost communication。没有可用日志指向 PR 代码错误。

修复建议:

  1. 环境问题,请 rerun。

关联变更: 未发现与 PR 修改直接相关的代码证据

🔴 Coverage build — 环境问题(置信度: 高)

分析器: 通用分析(fallback)

失败用例:

用例 错误摘要
Check coverage build size requires approval 访问 coverage_build_size 基线文件超时

关键日志:

curl: (28) Failed to connect to paddle-github-action.bj.bcebos.com port 443 after 130263 ms: Connection timed out
develop coverage build size not found: https://paddle-github-action.bj.bcebos.com/night/coverage/coverage_build_size
##[error]Process completed with exit code 1.
  • 根因摘要: 覆盖率基线下载网络超时

失败发生在读取 develop 分支 coverage build size 基线时,BOS 地址 443 端口连接超时后脚本退出。该错误属于外部存储/网络访问异常,与 PR 代码变更无直接关系。

修复建议:

  1. 环境问题,请 rerun。

关联变更: 未发现与 PR 修改直接相关的代码证据

🔴 Linux-DCU / Test — PR问题(置信度: 高)

分析器: 通用分析(fallback)

失败用例:

用例 错误摘要
test_dataloader_multiprocess.py::TestDataLoaderWindowsMultiprocess::test_multiprocess_more_workers DataLoader worker 被 SIGSEGV 杀死

关键日志:

FatalError: DataLoader process (pid 77481) exited is killed by signal: Segmentation fault.
test_dataloader_multiprocess failed
ERROR: test_multiprocess_more_workers (...)
File ".../test_dataloader_multiprocess.py", line 122, in test_multiprocess_more_workers
  loss_multi = self.run_simple_net(num_workers=4)
File ".../test_dataloader_multiprocess.py", line 81, in run_simple_net
  for data, label in loader():
File ".../dataloader_iter.py", line 861, in __next__
  self._reader.read_next_list()[0]
  • 根因摘要: 新增 DataLoader 多进程测试在 DCU 触发 worker 段错误

PR 新增 test/legacy_test/test_dataloader_multiprocess.py,其中 test_multiprocess_more_workers 在 Linux-DCU CI 上执行 num_workers=4。日志显示失败发生在 DataLoader 多进程读取路径,不是既有用例随机失败;该测试和 worker/mmap/DataLoader 相关代码均由本 PR 修改,判定为 PR 相关失败。

修复建议:

  1. 优先修复 python/paddle/io/dataloader/worker.py / paddle/fluid/imperative/data_loader.cc 中 worker 崩溃路径;若该用例仅用于 Windows 覆盖,应在 test/legacy_test/test_dataloader_multiprocess.py 对非 Windows 或 DCU 环境加明确 skip。

关联变更: test/legacy_test/test_dataloader_multiprocess.py:65, test/legacy_test/test_dataloader_multiprocess.py:81, test/legacy_test/test_dataloader_multiprocess.py:122, python/paddle/io/dataloader/worker.py:452, paddle/fluid/imperative/data_loader.cc:263

🔴 PR-CI-Inference / Build — 环境问题(置信度: 高)

分析器: 通用分析(fallback)

失败用例:

用例 错误摘要
Run build self-hosted runner 与 GitHub 失联,构建步骤停在 in_progress

关键日志:

The self-hosted runner lost communication with the server.
Run build: status=in_progress
Upload build.tar.gz: status=pending
Terminate and delete the container: status=pending
  • 根因摘要: Runner 通信中断导致 Inference 构建未完成

快速摘要明确为 self-hosted runner lost communication;深度状态也显示构建未正常结束,后续上传和清理步骤未执行。当前日志没有编译错误或测试断言失败证据。

修复建议:

  1. 环境问题,请 rerun。

关联变更: 未发现与 PR 修改直接相关的代码证据

🔴 Check approval — 需要 Approval(置信度: 高)

该 Job 需要人工 Approval,完成审批后 CI 才会继续执行。

修复建议:

  1. 请通过人工审批。

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。CloseHandle 早关的问题有继续推进,但当前 refcounted mmap 的 Windows HANDLE/view 生命周期仍有新的阻塞风险,细节已放在行级评论中。

前序 intptr_tint 的 Python metadata 收窄问题仍由既有 Nyanpasu 线程覆盖,本轮不重复开新线程。当前 Pre Commit 还因 test_modelaverage.py 的 ruff format 未通过,也需要同步处理。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

@@ -229,28 +349,64 @@ void RefcountedMemoryMapAllocation::close() {
flags_, map_size_ - mmap_alignment, ipc_name_, map_ptr_));
} else {
if (info->refcount == 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里把 Windows 的 CloseHandle 推迟到 info->refcount == 0,但 RefcountedMemoryMapAllocation 析构时会先调用这个 override,随后基类 MemoryMapAllocation::~MemoryMapAllocation() 还会调用基类 close();基类 close() 在检查 closed_ 之前执行 if (!closed_fd_) CloseHandle(...)。因此当 writer tensor 发送后 refcount 仍未归零时,这里不会设置 closed_fd_,基类析构仍会把 section HANDLE 提前关掉,reader 还没 OpenFileMappingA 的竞态仍存在;同时当前对象自己的 view 也没有 UnmapViewOfFile,会在 worker 持续出 batch 时泄漏映射。

请把 refcounted mapping 的 HANDLE/view 生命周期从基类 close() 中拆出来:refcount 未归零时不能让基类析构关闭该 HANDLE;若需要保留 HANDLE 让 reader 打开,也要转交给一个可在最终 refcount 归零或 worker 清理时关闭/Unmap 的 owner。示意:

const bool last_ref = (--info->refcount == 0);
#ifdef _WIN32
if (last_ref) {
  if (fd_ != -1 && !closed_fd_) {
    CloseHandle(reinterpret_cast<HANDLE>(fd_));
    closed_fd_ = true;
  }
  UnmapViewOfFile(map_ptr_);
} else {
  // Transfer fd_/map_ptr_ to a keeper that is cleaned when the reader
  // side has opened/finished, and prevent the base destructor from
  // closing this HANDLE in the meantime.
  TransferPendingMapping(ipc_name_, fd_, map_ptr_, map_size_);
  fd_ = -1;
  closed_fd_ = true;
}
#endif

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

更新:这次补丁在 refcount > 0 路径调用了 UnmapViewOfFile,所以前面提到的 mapped view 按 batch 泄漏已有推进;但 HANDLE 生命周期仍未收口。当前新分支只是设置 closed_fd_ = true 来阻止基类析构 CloseHandle,没有关闭 fd_,也没有把这个 HANDLE 转交给任何后续可清理的 owner。RefcountedMemoryMapAllocation 析构后这个 HANDLE 值随对象消失,只能等 worker 进程退出由 OS 清理;长时间 DataLoader 仍会按 batch 积累 CreateFileMapping HANDLE/section 对象。

请不要把进程退出作为正常释放路径。建议把 refcount > 0 时必须保留的 Windows HANDLE 放入一个明确的 keeper,并在 reader 已确认打开后或 worker 的 mmap cleanup 中关闭;或者改为可安全传递/复制的 HANDLE 方案,避免 sender allocation 析构后留下不可回收句柄。修复形态应类似:

if (info->refcount > 0) {
  WindowsMappingKeeper::Instance().Insert(ipc_name_, fd_);
  fd_ = -1;
  closed_fd_ = true;
  UnmapViewOfFile(map_ptr_);
}

// reader 已打开或 worker cleanup 时:
WindowsMappingKeeper::Instance().Close(ipc_name_);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里我不同意“与 Linux 对齐”的判断,关键差异在当前代码里可以直接看到:

  • Linux 默认不设置 MAPPED_KEEPFD 时并不会把 fd 泄漏到进程退出。POSIX 分支在 mmap_allocator.cc:226-235 里只有 MAPPED_KEEPFD 才保留 fd,否则 mmap 后立刻 ::close(fd) 并把 shared_fd 设为 -1。后续保持对象可打开的是 shm 名称和 shm_unlink 语义,不是 fd。
  • Windows 分支这次是主动把 CreateFileMappingA 返回的 HANDLE 保存到 fd_,因为当前注释也说明了它用于让 named section 在 reader OpenFileMappingA 前保持存活。当前 refcount > 0 分支只设置 closed_fd_ = trueUnmapViewOfFile,没有 CloseHandle(fd_),也没有把 fd_ 转交给任何后续清理 owner;对象析构后这个 HANDLE 值就丢失,只能等 worker 进程退出。
  • 因此长期运行或 persistent worker 场景下,Windows 会按 batch 累积 CreateFileMapping HANDLE/section owner;这不是 Linux 默认路径的行为。

WindowsMappingKeeper 不是必须的具体类名,但需要有等价的明确生命周期 owner:例如 reader 成功打开后能通知 sender 关闭保留 HANDLE,或 worker cleanup 能遍历并 CloseHandle 这些 pending handles。当前仅依赖进程退出回收,仍然不够。

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

更新:当前提交只在 info->refcount > 1 时立即 CloseHandle,但这没有覆盖主要泄漏路径。发送端 _reduce_lodtensor() 会先 _shared_incref();如果发送端 holder 在 reader rebuild 后释放,reader 侧 _new_shared_filename() 随即又调用 _shared_decref(),所以发送端 close() 里先 --info->refcount 后通常仍会落到 refcount == 1,进入 WindowsHandleKeeper::Insert()。如果 reader 还没打开,也同样是 refcount == 1。而当前 keeper 只有 Insert()/CloseAll(),没有在 reader 打开或 refcount 归零后按 ipc_name 关闭的路径;worker.py 的发送循环在 out_queue.put() 后也不会调用 _remove_tensor_list_mmap_fds,只在 finally_cleanup_mmap()。因此长 epoch / persistent worker 下,落入 refcount == 1 的 HANDLE 仍会按 batch 保留到 worker 退出,前面讨论的资源线性增长没有闭环。

建议把 refcount == 1 转交 keeper 的条目做成可回收状态,而不是只保存 HANDLE 到退出。例如 keeper 同时保存 base view 并在每次插入/周期性 cleanup 时回收 refcount == 0 的条目:

struct PendingMapping {
  intptr_t fd;
  void* map_ptr;
  size_t map_size;
};

void WindowsHandleKeeper::Insert(const std::string& ipc_name,
                                 intptr_t fd,
                                 void* map_ptr,
                                 size_t map_size) {
  SweepClosedMappings();
  handles_[ipc_name] = {fd, map_ptr, map_size};
}

void WindowsHandleKeeper::SweepClosedMappings() {
  for (auto it = handles_.begin(); it != handles_.end();) {
    auto* info = reinterpret_cast<CountInfo*>(it->second.map_ptr);
    if (info->refcount == 0) {
      UnmapViewOfFile(it->second.map_ptr);
      CloseHandle(reinterpret_cast<HANDLE>(it->second.fd));
      it = handles_.erase(it);
    } else {
      ++it;
    }
  }
}

对应地,refcount == 1 分支不要先 UnmapViewOfFile(map_ptr_) 后只保存 fd_;否则 keeper 后续无法判断 shared refcount,也无法在正常训练过程中释放这些 pending HANDLE。

PaddlePaddle-bot

This comment was marked as outdated.

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。test_modelaverage.py 的 ruff format 已修复;Windows refcount > 0 分支也已经补上 UnmapViewOfFile,前序 mapped view 泄漏问题有推进。

不过 refcounted mmap 的 Windows HANDLE 生命周期仍未完全收口:当前补丁会阻止基类析构关闭 HANDLE,但没有把该 HANDLE 交给后续可清理的 owner,细节已在既有 Nyanpasu 行级线程补充。前序 intptr_tint 的 metadata 收窄问题也仍由既有线程覆盖,本轮不重复开新线程。

当前 Pre Commit 还因 mmap_allocator.cc 新增 else 的 cpplint brace 规则未通过,需同步处理。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

@PlumBlossomMaid

Copy link
Copy Markdown
Contributor Author

@risemeup1111 关于 WindowsMappingKeeper 的分析:不需要。

理由:

  • Linux 端在不设置 MAPPED_KEEPFD 时,fd 同样泄漏到进程退出(默认 DataLoader 路径没有 MAPPED_KEEPFD)
  • Windows 端 refcount>0 时不关闭 HANDLE,行为与 Linux 对齐
  • 两个平台都在进程退出时由 OS 统一回收,长期运行模式(worker 反复出 batch)两个平台表现相同
  • 加 WindowsMappingKeeper 是过度设计,且 Linux 没有对应的 fd keeper

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。新增的 Windows worker 失败轮询入口方向是对的,但当前抛错路径没有先关闭/kill blocking queue,仍可能让主线程阻塞在读取端;细节已放在行级评论中。

前序 idx 初始化问题已修复;shared_fd metadata 宽度问题仍由既有 Nyanpasu 线程覆盖,本轮不重复展开。当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

Comment on lines +728 to +729
if sys.platform == 'win32':
core._throw_error_if_process_failed()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里直接调用 core._throw_error_if_process_failed() 后,如果 C++ 侧发现 Windows worker 非零退出并抛出 Fatal,异常会从 _get_data() 直接冒出到 reader 线程。_thread_loop() 在调用 _get_data() 的地方没有外层 try,因此不会执行 _exit_thread_unexpectedly()kill() blocking queue;主线程仍可能阻塞在 read_next() 上,只在后台线程日志里看到异常,达不到新增轮询的诊断目的。请在重新抛出前先走现有的异常退出路径,让 blocking queue 唤醒主线程。

Suggested change
if sys.platform == 'win32':
core._throw_error_if_process_failed()
if sys.platform == 'win32':
try:
core._throw_error_if_process_failed()
except Exception:
self._exit_thread_unexpectedly()
raise

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。上一轮 Windows worker 失败轮询抛错前未关闭/kill blocking queue 的问题已按建议修复:现在 core._throw_error_if_process_failed() 抛错时会先调用 _exit_thread_unexpectedly() 再重新抛出,可唤醒主线程读取端。

本轮没有新增行级评论。前序 shared_fd metadata 从 intptr_t 收窄到 int 的问题仍由既有 Nyanpasu 线程覆盖,本轮不重复展开;当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

PaddlePaddle-bot

This comment was marked as outdated.

PaddlePaddle-bot

This comment was marked as outdated.

@PlumBlossomMaid

Copy link
Copy Markdown
Contributor Author

@PaddlePaddle-bot F2 那个意见是假警报——test/legacy_test 下 753/753 个测试文件都有 if name == 'main',这是 Paddle 测试的标准写法。

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。MemoryMapAllocation::shared_fd() 返回类型已改为 intptr_t,但 shared_fd metadata 在 pybind 读写边界仍有 int 收窄,剩余问题已在既有 Nyanpasu 线程中补充说明,本轮不重复开新的行级评论。

前序 Windows worker 失败轮询和 idx 初始化问题仍看起来已修复;当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

PaddlePaddle-bot

This comment was marked as outdated.

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。shared_fd metadata 的 pybind 读写边界已同步改为 intptr_t,前序 Windows HANDLE 宽度收窄问题在当前代码中看起来已修复。

本轮没有新增行级评论。前序 Windows worker 失败轮询和 idx 初始化问题仍看起来已修复;当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

PaddlePaddle-bot

This comment was marked as outdated.

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。refcount > 1 时立即关闭 HANDLE 是一个改进,但 refcount == 1 进入 keeper 的条目仍没有正常训练过程中的回收路径,长时间运行仍可能累积 HANDLE/section;细节已补充在既有 Nyanpasu 行级线程中,本轮不重复开新行级评论。

前序 shared_fd metadata 宽度、Windows worker 失败轮询和 idx 初始化问题仍看起来已修复;当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

PaddlePaddle-bot

This comment was marked as outdated.

PaddlePaddle-bot

This comment was marked as outdated.

@liuhao2638 liuhao2638 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。前序 Windows HANDLE 生命周期问题已有推进,keeper 现在保存了 mapping view 以便按 refcount 回收;但这次新增的 keeper 插入路径存在自锁死风险,细节已放在新的行级评论中,需要修复后再合入。

前序 shared_fd metadata 宽度、Windows worker 失败轮询和 idx 初始化问题仍看起来已修复;当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

Comment on lines +687 to +688
std::lock_guard<std::mutex> lock(mtx_);
SweepClosedMappings();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里会在第一次需要把 refcount == 1 的 Windows mapping 转交给 keeper 时自锁死:Insert() 已持有 mtx_,随后调用的 SweepClosedMappings() 开头又用同一个 std::mutex 构造 lock_guardstd::mutex 不可重入,worker 会卡在这条 HANDLE 保活路径上,后续 batch 也无法送到主进程。

请把 sweep 放到加锁前,或拆出一个要求调用方已持锁的 SweepClosedMappingsLocked();不要在同一线程重复锁同一把 mutex。局部可先改成:

Suggested change
std::lock_guard<std::mutex> lock(mtx_);
SweepClosedMappings();
SweepClosedMappings();
std::lock_guard<std::mutex> lock(mtx_);

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。前序 WindowsHandleKeeper::Insert() 自锁问题已通过 locked helper 修复;refcount == 1 的 Windows HANDLE keeper 路径在当前代码中也已有可回收 owner。

本轮没有新增行级评论,未发现新的需要阻塞合入的问题;仍需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

PaddlePaddle-bot

This comment was marked as outdated.

@PlumBlossomMaid

Copy link
Copy Markdown
Contributor Author

@luotao1 方便安排人过来看看吗 我自己和写代码的Agent还有这几个评审Agent已经尽可能的把代码弄的好用好看了

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。FLAGS_use_shm_cache 的 cache 分支已经避免基类析构提前关闭 HANDLE,但当前补丁没有把该 HANDLE 转交给后续 owner,仍会在 worker 长时间运行时累积到进程退出;细节已放在新的行级评论中。

前序 WindowsHandleKeeper::Insert() 自锁问题仍看起来已修复;当前还需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

Comment on lines +342 to +345
#ifdef _WIN32
// Prevent base class MemoryMapAllocation::close() from closing the HANDLE
// (which would destroy the named section before the reader opens it).
closed_fd_ = true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 优先级:P1

这里阻止了基类析构关闭 HANDLE,但没有把 fd_ 转交给任何后续 owner。MemoryMapAllocationPool::Insert() 当前只保存 mmap_ptr_/name/size,MemoryMapAllocationPool::Clear() 在 Windows 下也只 UnmapViewOfFile(mmap.mmap_ptr_),不会 CloseHandle(fd_);而这个 allocation 析构后 fd_ 值就丢失了。这样虽然修掉了 reader 打开前被提前 CloseHandle 的竞态,但开启 FLAGS_use_shm_cache 后每个被放入 pool 的 mapping HANDLE 会保留到进程退出,长时间 worker 仍会累积内核 HANDLE。

请让 cache 分支也有明确的 HANDLE owner,例如把 fd_ 一并存进 MemoryMapInfo 并在 MemoryMapAllocationPool::Clear() 里关闭,或把该条目转交给 WindowsHandleKeeper/等价结构。修复形态可以是:

struct MemoryMapInfo {
  // ...
#ifdef _WIN32
  intptr_t fd_ = -1;
#endif
};

// pool 插入时保存 fd_,然后阻止基类重复关闭
MemoryMapAllocationPool::Instance().Insert(
    MemoryMapInfo(flags_, map_size_ - mmap_alignment, ipc_name_, map_ptr_, fd_));
fd_ = -1;
closed_fd_ = true;

// pool Clear():
UnmapViewOfFile(mmap.mmap_ptr_);
if (mmap.fd_ != -1) {
  CloseHandle(reinterpret_cast<HANDLE>(mmap.fd_));
}

PaddlePaddle-bot

This comment was marked as outdated.

@risemeup1111 risemeup1111 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已复查最新提交。FLAGS_use_shm_cache 的 cache 分支现在会把 Windows mapping HANDLE 随 MemoryMapInfo 交给 MemoryMapAllocationPool 持有,并在 pool cleanup 时关闭;MemoryMapFdSet::Clear() 也已移除会提前关闭 keeper 条目的 CloseAll(),前序 cache-path HANDLE owner/cleanup 竞态问题看起来已修复。

本轮没有新增行级评论,未发现新的需要阻塞合入的问题;仍需以最终 CI 和审批结果为准。

Powered by Nyanpasu with gpt-5.5 xhigh, please check the suggestions carefully.

@PaddlePaddle-bot PaddlePaddle-bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 Paddle-CI-Agent | pr_review | 2026-07-06 19:15:09

📋 Review 摘要

PR 概述:支持 Windows 平台 paddle.io.DataLoader(num_workers > 0),补齐 mmap allocator、pybind、worker 监控和测试。
变更范围paddle/phi/core/memory/allocationpaddle/fluid/imperativepaddle/fluid/pybindpython/paddle/io、DataLoader 测试。
影响面 Tag[Environment Adaptation] [Execute Infrastructure]

问题

未发现新的阻塞性问题。PR 规范问题在下面章节报,不要在这里重复

历史 Findings 修复情况

Finding 问题 状态
F1 test/** 新增 if __name__ == '__main__' 命中测试环境依赖信号。 ⚠️ 仍存在
F2 Windows worker cleanup 会提前关闭仍等待 reader 打开的共享内存。 ✅ 已修复

📝 PR 规范检查

符合规范;历史标题 Tag 建议已修复为 [Environment Adaptation] ...,描述模板和精度变化字段齐全。

总体评价

本轮按风险优先审查了共享内存生命周期、Windows worker 监控、pybind 导出、Python multiprocessing reductions 和新增测试。当前 diff 已针对历史 F2 增加 WindowsHandleKeeper,并避免 _cleanup_mmap_fds() 在 Windows 上直接关闭仍可能被 reader 打开的 mapping;未全量验证 Windows CI 运行结果。

@paddle-bot

paddle-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

你的PR提交成功,感谢你对开源项目的贡献!
请关注后续CI自动化测试结果,详情请参考Paddle-CI手册
Your PR has been submitted. Thanks for your contribution!
Please wait for the result of CI firstly. See Paddle CI Manual for details.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor External developers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants