mirror of
https://gitee.com/zhangsken/aliyunpan.git
synced 2026-10-16 02:23:49 +08:00
@@ -0,0 +1,7 @@
|
||||
package filelocker
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrTimeout = errors.New("timeout")
|
||||
)
|
||||
@@ -0,0 +1,25 @@
|
||||
package filelocker
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
const (
|
||||
lockExt = ".lock"
|
||||
)
|
||||
|
||||
type (
|
||||
FileLocker struct {
|
||||
FilePath string
|
||||
LockFilePath string
|
||||
lockFile *os.File
|
||||
}
|
||||
)
|
||||
|
||||
func NewFileLocker(path string) *FileLocker {
|
||||
return &FileLocker{
|
||||
FilePath: path,
|
||||
LockFilePath: path + lockExt,
|
||||
lockFile: nil,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package filelocker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestFlocker(t *testing.T) {
|
||||
// lock file first time - success
|
||||
locker := NewFileLocker("D:\\smb\\feny\\goprojects\\dev\\aliyunpan")
|
||||
e := LockFile(locker, 0755, true, 5*time.Second)
|
||||
fmt.Println(e)
|
||||
|
||||
// lock file again - fail
|
||||
//time.Sleep(5 * time.Second)
|
||||
//e = flock(locker, 0755, true, 5*time.Second)
|
||||
//fmt.Println(e)
|
||||
|
||||
// Unlock the file.
|
||||
if err := UnlockFile(locker); err != nil {
|
||||
log.Printf("funlock error: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package filelocker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LockFile acquires an advisory lock on a file descriptor.
|
||||
func LockFile(locker *FileLocker, mode os.FileMode, exclusive bool, timeout time.Duration) error {
|
||||
f, err := os.OpenFile(locker.LockFilePath, os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
locker.lockFile = f
|
||||
|
||||
var t time.Time
|
||||
for {
|
||||
// If we're beyond our timeout then return an error.
|
||||
// This can only occur after we've attempted a flock once.
|
||||
if t.IsZero() {
|
||||
t = time.Now()
|
||||
} else if timeout > 0 && time.Since(t) > timeout {
|
||||
return ErrTimeout
|
||||
}
|
||||
var lock syscall.Flock_t
|
||||
lock.Start = 0
|
||||
lock.Len = 0
|
||||
lock.Pid = 0
|
||||
lock.Whence = 0
|
||||
lock.Pid = 0
|
||||
if exclusive {
|
||||
lock.Type = syscall.F_WRLCK
|
||||
} else {
|
||||
lock.Type = syscall.F_RDLCK
|
||||
}
|
||||
err := syscall.FcntlFlock(locker.lockFile.Fd(), syscall.F_SETLK, &lock)
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if err != syscall.EAGAIN {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for a bit and try again.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// UnlockFile releases an advisory lock on a file descriptor.
|
||||
func UnlockFile(locker *FileLocker) error {
|
||||
var lock syscall.Flock_t
|
||||
lock.Start = 0
|
||||
lock.Len = 0
|
||||
lock.Type = syscall.F_UNLCK
|
||||
lock.Whence = 0
|
||||
return syscall.FcntlFlock(uintptr(locker.lockFile.Fd()), syscall.F_SETLK, &lock)
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
//go:build !windows && !plan9 && !solaris
|
||||
// +build !windows,!plan9,!solaris
|
||||
|
||||
package filelocker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
// LockFile acquires an advisory lock on a file descriptor.
|
||||
func LockFile(locker *FileLocker, mode os.FileMode, exclusive bool, timeout time.Duration) error {
|
||||
f, err := os.OpenFile(locker.LockFilePath, os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
locker.lockFile = f
|
||||
|
||||
var t time.Time
|
||||
for {
|
||||
// If we're beyond our timeout then return an error.
|
||||
// This can only occur after we've attempted a flock once.
|
||||
if t.IsZero() {
|
||||
t = time.Now()
|
||||
} else if timeout > 0 && time.Since(t) > timeout {
|
||||
return ErrTimeout
|
||||
}
|
||||
flag := syscall.LOCK_SH
|
||||
if exclusive {
|
||||
flag = syscall.LOCK_EX
|
||||
}
|
||||
|
||||
// Otherwise attempt to obtain an exclusive lock.
|
||||
err := syscall.Flock(int(locker.lockFile.Fd()), flag|syscall.LOCK_NB)
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if err != syscall.EWOULDBLOCK {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for a bit and try again.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// UnlockFile releases an advisory lock on a file descriptor.
|
||||
func UnlockFile(locker *FileLocker) error {
|
||||
return syscall.Flock(int(locker.lockFile.Fd()), syscall.LOCK_UN)
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package filelocker
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// LockFileEx code derived from golang build filemutex_windows.go @ v1.5.1
|
||||
var (
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procLockFileEx = modkernel32.NewProc("LockFileEx")
|
||||
procUnlockFileEx = modkernel32.NewProc("UnlockFileEx")
|
||||
)
|
||||
|
||||
const (
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/aa365203(v=vs.85).aspx
|
||||
flagLockExclusive = 2
|
||||
flagLockFailImmediately = 1
|
||||
|
||||
// see https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
|
||||
errLockViolation syscall.Errno = 0x21
|
||||
)
|
||||
|
||||
func lockFileEx(h syscall.Handle, flags, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
|
||||
r, _, err := procLockFileEx.Call(uintptr(h), uintptr(flags), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)))
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func unlockFileEx(h syscall.Handle, reserved, locklow, lockhigh uint32, ol *syscall.Overlapped) (err error) {
|
||||
r, _, err := procUnlockFileEx.Call(uintptr(h), uintptr(reserved), uintptr(locklow), uintptr(lockhigh), uintptr(unsafe.Pointer(ol)), 0)
|
||||
if r == 0 {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// LockFile acquires an advisory lock on a file descriptor.
|
||||
func LockFile(locker *FileLocker, mode os.FileMode, exclusive bool, timeout time.Duration) error {
|
||||
// Create a separate lock file on windows because a process
|
||||
// cannot share an exclusive lock on the same file. This is
|
||||
// needed during Tx.WriteTo().
|
||||
f, err := os.OpenFile(locker.LockFilePath, os.O_CREATE, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
locker.lockFile = f
|
||||
|
||||
var t time.Time
|
||||
for {
|
||||
// If we're beyond our timeout then return an error.
|
||||
// This can only occur after we've attempted a flock once.
|
||||
if t.IsZero() {
|
||||
t = time.Now()
|
||||
} else if timeout > 0 && time.Since(t) > timeout {
|
||||
return ErrTimeout
|
||||
}
|
||||
|
||||
var flag uint32 = flagLockFailImmediately
|
||||
if exclusive {
|
||||
flag |= flagLockExclusive
|
||||
}
|
||||
|
||||
err := lockFileEx(syscall.Handle(locker.lockFile.Fd()), flag, 0, 1, 0, &syscall.Overlapped{})
|
||||
if err == nil {
|
||||
return nil
|
||||
} else if err != errLockViolation {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for a bit and try again.
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
// UnlockFile releases an advisory lock on a file descriptor.
|
||||
func UnlockFile(locker *FileLocker) error {
|
||||
err := unlockFileEx(syscall.Handle(locker.lockFile.Fd()), 0, 1, 0, &syscall.Overlapped{})
|
||||
locker.lockFile.Close()
|
||||
os.Remove(locker.LockFilePath)
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user