Skip to content

Commit

Permalink
Windows blocking locks. (#200)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncruces authored Dec 11, 2024
1 parent 5f1d572 commit 7f5ea54
Show file tree
Hide file tree
Showing 7 changed files with 52 additions and 23 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
3 changes: 3 additions & 0 deletions util/osutil/open_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
43 changes: 27 additions & 16 deletions vfs/os_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
package vfs

import (
"math/rand"
"os"
"time"

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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 {
Expand All @@ -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
}
}
Expand Down
2 changes: 1 addition & 1 deletion vfs/shm_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
12 changes: 9 additions & 3 deletions vfs/shm_ofd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type vfsShm struct {
path string
regions []*util.MappedRegion
readOnly bool
fileLock bool
blocking bool
sync.Mutex
}
Expand All @@ -29,17 +30,20 @@ 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 {
return _CANTOPEN
}
s.File = f
}
if s.fileLock {
return _OK
}

// Dead man's switch.
if lock, rc := osTestLock(s.File, _SHM_DMS, 1); rc != _OK {
Expand All @@ -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) {
Expand Down
12 changes: 10 additions & 2 deletions vfs/shm_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"io"
"os"
"sync"
"syscall"
"time"

"github.com/tetratelabs/wazero/api"
Expand All @@ -27,6 +28,7 @@ type vfsShm struct {
shadow [][_WALINDEX_PGSZ]byte
ptrs []uint32
stack [1]uint64
fileLock bool
blocking bool
sync.Mutex
}
Expand All @@ -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 {
Expand All @@ -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) {
Expand Down

0 comments on commit 7f5ea54

Please sign in to comment.