|
@@ -89,26 +89,33 @@ func OpenFileReadOnly(path string) (*File, error) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (ff *File) Close() error {
|
|
func (ff *File) Close() error {
|
|
|
- // defer recover: 防止 bp.Close() / bp.Flush() 里的 diskWrite panic 把 unlock 跳过 ——
|
|
|
|
|
- // Windows LockFileEx 是 OS 级锁, 进程崩了句柄会被系统回收, 但同一进程里 panic
|
|
|
|
|
- // 只会 defer 触发, 不会释放 OS 资源, 所以必须 recover 保证 unlock 执行。
|
|
|
|
|
- defer func() {
|
|
|
|
|
- if r := recover(); r != nil {
|
|
|
|
|
- ff.unlock()
|
|
|
|
|
- ff.f.Close()
|
|
|
|
|
- panic(r) // 让上层知道 Close 中间炸过, 不要静默
|
|
|
|
|
- }
|
|
|
|
|
- }()
|
|
|
|
|
|
|
+ // 顺序: 先刷完脏页 → 释放文件锁 → 关闭句柄。
|
|
|
|
|
+ //
|
|
|
|
|
+ // diskWrite (底层 os.File.WriteAt) 只返回 error, 不会 panic —— 之前脑补的
|
|
|
|
|
+ // "Flush panic 跳过 unlock" 场景不存在, 不需要 defer recover。
|
|
|
|
|
+ // 进程级 panic 没人 recover 时 Go runtime 直接 exit → OS 内核自动 CloseHandle
|
|
|
|
|
+ // 所有句柄 → 文件锁自动释放, 也不会残留。
|
|
|
|
|
|
|
|
|
|
+ var bpErr error
|
|
|
if ff.bp != nil {
|
|
if ff.bp != nil {
|
|
|
- ff.bp.Close() // 先刷盘再关文件
|
|
|
|
|
|
|
+ bpErr = ff.bp.Close() // 刷完所有脏页, 返回第一个写盘错误
|
|
|
}
|
|
}
|
|
|
// 显式释放跨进程文件锁 (若持有); 句柄关闭也会自动释放, 这里兜底。
|
|
// 显式释放跨进程文件锁 (若持有); 句柄关闭也会自动释放, 这里兜底。
|
|
|
- // 放在 bp.Close() 之后: bp.Flush 里的 writePageDisk 可能让 EOF 改变,
|
|
|
|
|
- // unlock 必须用 tryLock 时保存的原始 lockOffset —— 顺序对 unlock 本身无影响,
|
|
|
|
|
- // 但语义上"释放锁"应该在"写完所有页"之后。
|
|
|
|
|
|
|
+ // 放在 bp.Close() 之后: bp.Flush 里的 writePageDisk 可能改变 EOF,
|
|
|
|
|
+ // unlock 用的是 tryLock 时保存的 lockOffset —— 顺序不影响 unlock 正确性,
|
|
|
|
|
+ // 但语义上"写完所有页再释放锁"。
|
|
|
ff.unlock()
|
|
ff.unlock()
|
|
|
- return ff.f.Close()
|
|
|
|
|
|
|
+
|
|
|
|
|
+ fErr := ff.f.Close()
|
|
|
|
|
+
|
|
|
|
|
+ // 合并错误: bp.Flush 的写盘失败 (可能有脏页还没持久化) + 句柄关闭错误
|
|
|
|
|
+ if bpErr != nil {
|
|
|
|
|
+ if fErr != nil {
|
|
|
|
|
+ return fmt.Errorf("bp flush 错误: %w; 文件 close 错误: %v", bpErr, fErr)
|
|
|
|
|
+ }
|
|
|
|
|
+ return bpErr
|
|
|
|
|
+ }
|
|
|
|
|
+ return fErr
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// InitBufferPool 初始化页面缓冲池, 拦截 ReadPage/WritePage 实现内存缓存
|
|
// InitBufferPool 初始化页面缓冲池, 拦截 ReadPage/WritePage 实现内存缓存
|