mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Windows blocking locks. (#200)
This commit is contained in:
@@ -10,7 +10,7 @@ as well as direct access to most of the [C SQLite API](https://sqlite.org/cintro
|
||||
|
||||
It wraps a [Wasm](https://webassembly.org/) [build](embed/) of SQLite,
|
||||
and uses [wazero](https://wazero.io/) as the runtime.\
|
||||
Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ runtime dependencies.
|
||||
Go, wazero and [`x/sys`](https://pkg.go.dev/golang.org/x/sys) are the _only_ direct dependencies.
|
||||
|
||||
### Getting started
|
||||
|
||||
|
||||
@@ -10,5 +10,6 @@ golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
|
||||
@@ -101,6 +101,9 @@ func syscallOpen(path string, mode int, perm uint32) (fd Handle, err error) {
|
||||
const _FILE_FLAG_WRITE_THROUGH = 0x80000000
|
||||
attrs |= _FILE_FLAG_WRITE_THROUGH
|
||||
}
|
||||
if mode&O_NONBLOCK != 0 {
|
||||
attrs |= FILE_FLAG_OVERLAPPED
|
||||
}
|
||||
return CreateFile(pathp, access, sharemode, sa, createmode, attrs, 0)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
@@ -46,7 +45,7 @@ func osGetExclusiveLock(file *os.File, state *LockLevel) _ErrorCode {
|
||||
osUnlock(file, _SHARED_FIRST, _SHARED_SIZE)
|
||||
|
||||
// Acquire the EXCLUSIVE lock.
|
||||
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, time.Millisecond)
|
||||
rc := osWriteLock(file, _SHARED_FIRST, _SHARED_SIZE, 0)
|
||||
|
||||
if rc != _OK {
|
||||
// Reacquire the SHARED lock.
|
||||
@@ -123,29 +122,40 @@ func osLock(file *os.File, flags, start, len uint32, timeout time.Duration, def
|
||||
var err error
|
||||
switch {
|
||||
case timeout == 0:
|
||||
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
|
||||
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len, 0)
|
||||
case timeout < 0:
|
||||
err = osLockEx(file, flags, start, len)
|
||||
err = osLockEx(file, flags, start, len, 0)
|
||||
default:
|
||||
before := time.Now()
|
||||
for {
|
||||
err = osLockEx(file, flags|windows.LOCKFILE_FAIL_IMMEDIATELY, start, len)
|
||||
if errno, _ := err.(windows.Errno); errno != windows.ERROR_LOCK_VIOLATION {
|
||||
break
|
||||
var event windows.Handle
|
||||
event, err = windows.CreateEvent(nil, 1, 0, nil)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
defer windows.CloseHandle(event)
|
||||
|
||||
err = osLockEx(file, flags, start, len, event)
|
||||
if err == windows.ERROR_IO_PENDING {
|
||||
rc, serr := windows.WaitForSingleObject(event, uint32(timeout/time.Millisecond))
|
||||
if rc == windows.WAIT_OBJECT_0 {
|
||||
return _OK
|
||||
}
|
||||
if time.Since(before) > timeout {
|
||||
break
|
||||
if serr != nil {
|
||||
err = serr
|
||||
} else {
|
||||
err = windows.Errno(rc)
|
||||
}
|
||||
const sleepIncrement = 1024*1024 - 1 // power of two, ~1ms
|
||||
time.Sleep(time.Duration(rand.Int63() & sleepIncrement))
|
||||
windows.CancelIo(windows.Handle(file.Fd()))
|
||||
}
|
||||
}
|
||||
return osLockErrorCode(err, def)
|
||||
}
|
||||
|
||||
func osLockEx(file *os.File, flags, start, len uint32) error {
|
||||
func osLockEx(file *os.File, flags, start, len uint32, event windows.Handle) error {
|
||||
return windows.LockFileEx(windows.Handle(file.Fd()), flags,
|
||||
0, len, 0, &windows.Overlapped{Offset: start})
|
||||
0, len, 0, &windows.Overlapped{
|
||||
Offset: start,
|
||||
HEvent: event,
|
||||
})
|
||||
}
|
||||
|
||||
func osReadLock(file *os.File, start, len uint32, timeout time.Duration) _ErrorCode {
|
||||
@@ -166,7 +176,8 @@ func osLockErrorCode(err error, def _ErrorCode) _ErrorCode {
|
||||
case
|
||||
windows.ERROR_LOCK_VIOLATION,
|
||||
windows.ERROR_IO_PENDING,
|
||||
windows.ERROR_OPERATION_ABORTED:
|
||||
windows.ERROR_OPERATION_ABORTED,
|
||||
windows.WAIT_TIMEOUT:
|
||||
return _BUSY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
|
||||
// Always open file read-write, as it will be shared.
|
||||
f, err := os.OpenFile(s.path,
|
||||
unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ type vfsShm struct {
|
||||
path string
|
||||
regions []*util.MappedRegion
|
||||
readOnly bool
|
||||
fileLock bool
|
||||
blocking bool
|
||||
sync.Mutex
|
||||
}
|
||||
@@ -29,10 +30,10 @@ var _ blockingSharedMemory = &vfsShm{}
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
f, err := os.OpenFile(s.path,
|
||||
unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
os.O_RDWR|os.O_CREATE|_O_NOFOLLOW, 0666)
|
||||
if err != nil {
|
||||
f, err = os.OpenFile(s.path,
|
||||
unix.O_RDONLY|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
os.O_RDONLY|os.O_CREATE|_O_NOFOLLOW, 0666)
|
||||
s.readOnly = true
|
||||
}
|
||||
if err != nil {
|
||||
@@ -40,6 +41,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
}
|
||||
s.File = f
|
||||
}
|
||||
if s.fileLock {
|
||||
return _OK
|
||||
}
|
||||
|
||||
// Dead man's switch.
|
||||
if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
|
||||
@@ -64,7 +68,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return _IOERR_SHMOPEN
|
||||
}
|
||||
}
|
||||
return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
s.fileLock = rc == _OK
|
||||
return rc
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
@@ -27,6 +28,7 @@ type vfsShm struct {
|
||||
shadow [][_WALINDEX_PGSZ]byte
|
||||
ptrs []uint32
|
||||
stack [1]uint64
|
||||
fileLock bool
|
||||
blocking bool
|
||||
sync.Mutex
|
||||
}
|
||||
@@ -46,12 +48,16 @@ func (s *vfsShm) Close() error {
|
||||
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
f, err := osutil.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
|
||||
f, err := osutil.OpenFile(s.path,
|
||||
os.O_RDWR|os.O_CREATE|syscall.O_NONBLOCK, 0666)
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
s.File = f
|
||||
}
|
||||
if s.fileLock {
|
||||
return _OK
|
||||
}
|
||||
|
||||
// Dead man's switch.
|
||||
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
|
||||
@@ -61,7 +67,9 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return _IOERR_SHMOPEN
|
||||
}
|
||||
}
|
||||
return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
s.fileLock = rc == _OK
|
||||
return rc
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ uint32, rc _ErrorCode) {
|
||||
|
||||
Reference in New Issue
Block a user