1
0
mirror of https://github.com/Mrs4s/go-cqhttp.git synced 2025-05-06 20:13:50 +08:00

internal/btree: implement file lock

For #1366
This commit is contained in:
wdvxdr 2022-02-19 18:55:28 +08:00
parent 75bed6aabc
commit e6904d8dde
No known key found for this signature in database
GPG Key ID: 703F8C071DE7A1B6
3 changed files with 91 additions and 0 deletions

View File

@ -18,6 +18,10 @@ const (
tableStructSize = int(unsafe.Sizeof(table{})) tableStructSize = int(unsafe.Sizeof(table{}))
) )
type fileLock interface {
release() error
}
type item struct { type item struct {
hash [hashSize]byte hash [hashSize]byte
offset int64 offset int64
@ -48,6 +52,7 @@ type DB struct {
alloc int64 alloc int64
cache [cacheSlots]cache cache [cacheSlots]cache
flock fileLock
inAllocator bool inAllocator bool
deleteLarger bool deleteLarger bool
fqueue [freeQueueLen]chunk fqueue [freeQueueLen]chunk
@ -108,6 +113,10 @@ func (d *DB) flushSuper() {
// Open opens an existed btree file // Open opens an existed btree file
func Open(name string) (*DB, error) { func Open(name string) (*DB, error) {
lock, err := newFileLock(name + ".lock")
if err != nil {
return nil, errors.New("文件被其他进程占用")
}
btree := new(DB) btree := new(DB)
fd, err := os.OpenFile(name, os.O_RDWR, 0o644) fd, err := os.OpenFile(name, os.O_RDWR, 0o644)
if err != nil { if err != nil {
@ -120,17 +129,23 @@ func Open(name string) (*DB, error) {
btree.top = super.top btree.top = super.top
btree.freeTop = super.freeTop btree.freeTop = super.freeTop
btree.alloc = super.alloc btree.alloc = super.alloc
btree.flock = lock
return btree, errors.Wrap(err, "btree read meta info failed") return btree, errors.Wrap(err, "btree read meta info failed")
} }
// Create creates a database // Create creates a database
func Create(name string) (*DB, error) { func Create(name string) (*DB, error) {
lock, err := newFileLock(name + ".lock")
if err != nil {
return nil, errors.New("文件被其他进程占用")
}
btree := new(DB) btree := new(DB)
fd, err := os.OpenFile(name, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o644) fd, err := os.OpenFile(name, os.O_RDWR|os.O_TRUNC|os.O_CREATE, 0o644)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "btree open file failed") return nil, errors.Wrap(err, "btree open file failed")
} }
btree.flock = lock
btree.fd = fd btree.fd = fd
btree.alloc = int64(superSize) btree.alloc = int64(superSize)
btree.flushSuper() btree.flushSuper()
@ -140,6 +155,9 @@ func Create(name string) (*DB, error) {
// Close closes the database // Close closes the database
func (d *DB) Close() error { func (d *DB) Close() error {
_ = d.fd.Sync() _ = d.fd.Sync()
if err := d.flock.release(); err != nil {
return err
}
err := d.fd.Close() err := d.fd.Close()
for i := 0; i < cacheSlots; i++ { for i := 0; i < cacheSlots; i++ {
d.cache[i] = cache{} d.cache[i] = cache{}

View File

@ -0,0 +1,45 @@
//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd
package btree
import (
"os"
"syscall"
)
type unixFileLock struct {
f *os.File
}
func (fl *unixFileLock) release() error {
if err := setFileLock(fl.f, false); err != nil {
return err
}
return fl.f.Close()
}
func newFileLock(path string) (fl fileLock, err error) {
flag := os.O_RDWR
f, err := os.OpenFile(path, flag, 0)
if os.IsNotExist(err) {
f, err = os.OpenFile(path, flag|os.O_CREATE, 0644)
}
if err != nil {
return
}
err = setFileLock(f, true)
if err != nil {
f.Close()
return
}
fl = &unixFileLock{f: f}
return
}
func setFileLock(f *os.File, lock bool) error {
how := syscall.LOCK_UN
if lock {
how = syscall.LOCK_EX
}
return syscall.Flock(int(f.Fd()), how|syscall.LOCK_NB)
}

View File

@ -0,0 +1,28 @@
package btree
import "syscall"
type windowsFileLock struct {
fd syscall.Handle
}
func (fl *windowsFileLock) release() error {
return syscall.Close(fl.fd)
}
func newFileLock(path string) (fileLock, error) {
pathp, err := syscall.UTF16PtrFromString(path)
if err != nil {
return nil, err
}
const access uint32 = syscall.GENERIC_READ | syscall.GENERIC_WRITE
fd, err := syscall.CreateFile(pathp, access, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_ATTRIBUTE_NORMAL, 0)
if err == syscall.ERROR_FILE_NOT_FOUND {
fd, err = syscall.CreateFile(pathp, access, 0, nil, syscall.OPEN_ALWAYS, syscall.FILE_ATTRIBUTE_NORMAL, 0)
}
if err != nil {
return nil, err
}
return &windowsFileLock{fd: fd}, nil
}