| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124 |
- //go:build windows
- package core
- // Windows 跨进程文件锁: 用 kernel32 的 LockFileEx / UnlockFileEx 对文件的一段字节
- // 区域加排他锁。纯标准库 (syscall 直接调 DLL), 不引入 golang.org/x/sys。
- import (
- "syscall"
- "unsafe"
- )
- // 锁定区域: 文件偏移 0 起 lockByteCount 字节。加锁时文件至少已写入 4KB 头页
- // (新库在 WriteHeader 之后才 tryLock), 所以锁 [0, lockByteCount) 不会超出文件
- // 末尾。双方约定锁同一区域即可互斥 (与 Unix flock 的整文件锁语义等价)。
- const lockByteCount = 1
- const (
- winLockFileExclusive = 0x00000002 // LOCKFILE_EXCLUSIVE_LOCK
- winLockFileFailImmediately = 0x00000001 // LOCKFILE_FAIL_IMMEDIATELY (非阻塞)
- winErrLockViolation = 33 // ERROR_LOCK_VIOLATION
- )
- var (
- kernel32 = syscall.NewLazyDLL("kernel32.dll")
- procLockFileEx = kernel32.NewProc("LockFileEx")
- procUnlockFileEx = kernel32.NewProc("UnlockFileEx")
- )
- // lockOverlapped 构造指向文件偏移 0 的 OVERLAPPED (LockFileEx 用它指定锁起点)。
- func lockOverlapped() *syscall.Overlapped {
- var ol syscall.Overlapped
- ol.Offset = 0
- ol.OffsetHigh = 0
- return &ol
- }
- func (ff *File) winHandle() (syscall.Handle, error) {
- sc, err := ff.f.SyscallConn()
- if err != nil {
- return 0, err
- }
- var h syscall.Handle
- if err := sc.Control(func(fd uintptr) {
- h = syscall.Handle(fd)
- }); err != nil {
- return 0, err
- }
- return h, nil
- }
- // tryLock 非阻塞地对文件区域加排他锁; 已被其它进程 (或本进程其它句柄) 锁定时
- // 返回 ErrDatabaseLocked。
- func (ff *File) tryLock() error {
- if ff.fileLocked {
- return nil
- }
- h, err := ff.winHandle()
- if err != nil {
- return err
- }
- ol := lockOverlapped()
- r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6,
- uintptr(h),
- uintptr(winLockFileExclusive|winLockFileFailImmediately),
- 0,
- uintptr(lockByteCount),
- 0,
- uintptr(unsafe.Pointer(ol)))
- if r1 == 0 {
- if e1 == syscall.Errno(winErrLockViolation) {
- return ErrDatabaseLocked
- }
- return e1
- }
- ff.fileLocked = true
- return nil
- }
- // tryRLock 非阻塞地对文件区域加【共享锁】(只读打开): 不加 EXCLUSIVE 标志。
- // 共享锁之间可共存, 与排他锁 (tryLock) 互斥; 冲突返回 ErrDatabaseLocked。
- func (ff *File) tryRLock() error {
- if ff.fileLocked {
- return nil
- }
- h, err := ff.winHandle()
- if err != nil {
- return err
- }
- ol := lockOverlapped()
- r1, _, e1 := syscall.Syscall6(procLockFileEx.Addr(), 6,
- uintptr(h),
- uintptr(winLockFileFailImmediately), // 共享锁 (无 LOCKFILE_EXCLUSIVE_LOCK)
- 0,
- uintptr(lockByteCount),
- 0,
- uintptr(unsafe.Pointer(ol)))
- if r1 == 0 {
- if e1 == syscall.Errno(winErrLockViolation) {
- return ErrDatabaseLocked
- }
- return e1
- }
- ff.fileLocked = true
- return nil
- }
- // unlock 释放文件锁 (Close 路径调用; 错误忽略 —— 句柄关闭同样会释放)。
- func (ff *File) unlock() {
- if !ff.fileLocked {
- return
- }
- if h, err := ff.winHandle(); err == nil {
- ol := lockOverlapped()
- _, _, _ = syscall.Syscall6(procUnlockFileEx.Addr(), 5,
- uintptr(h),
- 0,
- uintptr(lockByteCount),
- 0,
- uintptr(unsafe.Pointer(ol)),
- 0)
- }
- ff.fileLocked = false
- }
|