Improved WAL API.

This commit is contained in:
Nuno Cruces
2024-04-27 20:53:54 +01:00
parent 82d8a2d796
commit 992676d7ec
4 changed files with 69 additions and 41 deletions

View File

@@ -3,6 +3,7 @@ package vfs
import (
"context"
"io"
"github.com/tetratelabs/wazero/api"
)
@@ -154,17 +155,19 @@ type FileCheckpoint interface {
CheckpointStart() error
}
// FileSharedMemory extends File to possibly implement shared memory.
// FileSharedMemory extends File to possibly implement
// shared-memory for the WAL-index.
// It's OK for SharedMemory to return nil.
type FileSharedMemory interface {
File
SharedMemory() SharedMemory
}
// SharedMemory is a shared memory implementation.
// SharedMemory is a shared-memory WAL-index implementation.
// This cannot be externally implemented.
type SharedMemory interface {
shmMap(context.Context, api.Module, int32, int32, bool) (uint32, error)
shmLock(int32, int32, _ShmFlag) error
shmUnmap(bool)
io.Closer
}

View File

@@ -118,13 +118,14 @@ func (vfsOS) OpenFilename(name *Filename, flags OpenFlag) (File, OpenFlag, error
syncDir: runtime.GOOS != "windows" &&
flags&(OPEN_CREATE) != 0 &&
flags&(OPEN_MAIN_JOURNAL|OPEN_SUPER_JOURNAL|OPEN_WAL) != 0,
shm: NewSharedMemory(name.String()+"-shm", flags),
}
return &file, flags, nil
}
type vfsFile struct {
*os.File
shm vfsShm
shm SharedMemory
lock LockLevel
readOnly bool
keepWAL bool
@@ -142,7 +143,9 @@ var (
)
func (f *vfsFile) Close() error {
f.shm.Close()
if f.shm != nil {
f.shm.Close()
}
return f.File.Close()
}

View File

@@ -22,90 +22,107 @@ import (
// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode
const SupportsSharedMemory = true
type vfsShm struct {
*os.File
regions []*util.MappedRegion
}
const (
_SHM_NLOCK = 8
_SHM_BASE = 120
_SHM_DMS = _SHM_BASE + _SHM_NLOCK
)
func (f *vfsFile) SharedMemory() SharedMemory { return f }
func (f *vfsFile) SharedMemory() SharedMemory { return f.shm }
func (f *vfsFile) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) {
// NewSharedMemory returns a shared-memory WAL-index
// backed a file with the given path.
// It may return nil if shared-memory is not supported,
// or not appropriate for the given flags.
// Only [OPEN_MAIN_DB] databases support WAL mode.
func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
if flags&OPEN_MAIN_DB == 0 {
return nil
}
return &vfsShm{
path: path,
readOnly: flags&OPEN_READONLY != 0,
}
}
type vfsShm struct {
*os.File
regions []*util.MappedRegion
path string
readOnly bool
}
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, error) {
// Ensure size is a multiple of the OS page size.
if int(size)&(unix.Getpagesize()-1) != 0 {
return 0, _IOERR_SHMMAP
}
if f.shm.File == nil {
if s.File == nil {
var flag int
if f.readOnly {
if s.readOnly {
flag = unix.O_RDONLY
} else {
flag = unix.O_RDWR
}
s, err := os.OpenFile(f.Name()+"-shm",
f, err := os.OpenFile(s.path,
flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
if err != nil {
return 0, _CANTOPEN
}
f.shm.File = s
s.File = f
}
// Dead man's switch.
if lock, rc := osGetLock(f.shm.File, _SHM_DMS, 1); rc != _OK {
if lock, rc := osGetLock(s.File, _SHM_DMS, 1); rc != _OK {
return 0, _IOERR_LOCK
} else if lock == unix.F_WRLCK {
return 0, _BUSY
} else if lock == unix.F_UNLCK {
if f.readOnly {
if s.readOnly {
return 0, _READONLY_CANTINIT
}
if rc := osWriteLock(f.shm.File, _SHM_DMS, 1, 0); rc != _OK {
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
return 0, rc
}
if err := f.shm.Truncate(0); err != nil {
if err := s.Truncate(0); err != nil {
return 0, _IOERR_SHMOPEN
}
}
if rc := osReadLock(f.shm.File, _SHM_DMS, 1, 0); rc != _OK {
if rc := osReadLock(s.File, _SHM_DMS, 1, 0); rc != _OK {
return 0, rc
}
// Check if file is big enough.
s, err := f.shm.Seek(0, io.SeekEnd)
o, err := s.Seek(0, io.SeekEnd)
if err != nil {
return 0, _IOERR_SHMSIZE
}
if n := (int64(id) + 1) * int64(size); n > s {
if n := (int64(id) + 1) * int64(size); n > o {
if !extend {
return 0, nil
}
err := osAllocate(f.shm.File, n)
err := osAllocate(s.File, n)
if err != nil {
return 0, _IOERR_SHMSIZE
}
}
var prot int
if f.readOnly {
if s.readOnly {
prot = unix.PROT_READ
} else {
prot = unix.PROT_READ | unix.PROT_WRITE
}
r, err := util.MapRegion(ctx, mod, f.shm.File, int64(id)*int64(size), size, prot)
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot)
if err != nil {
return 0, err
}
f.shm.regions = append(f.shm.regions, r)
s.regions = append(s.regions, r)
return r.Ptr, nil
}
func (f *vfsFile) shmLock(offset, n int32, flags _ShmFlag) error {
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) error {
// Argument check.
if n <= 0 || offset < 0 || offset+n > _SHM_NLOCK {
panic(util.AssertErr())
@@ -126,28 +143,28 @@ func (f *vfsFile) shmLock(offset, n int32, flags _ShmFlag) error {
switch {
case flags&_SHM_UNLOCK != 0:
return osUnlock(f.shm.File, _SHM_BASE+int64(offset), int64(n))
return osUnlock(s.File, _SHM_BASE+int64(offset), int64(n))
case flags&_SHM_SHARED != 0:
return osReadLock(f.shm.File, _SHM_BASE+int64(offset), int64(n), 0)
return osReadLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
case flags&_SHM_EXCLUSIVE != 0:
return osWriteLock(f.shm.File, _SHM_BASE+int64(offset), int64(n), 0)
return osWriteLock(s.File, _SHM_BASE+int64(offset), int64(n), 0)
default:
panic(util.AssertErr())
}
}
func (f *vfsFile) shmUnmap(delete bool) {
func (s *vfsShm) shmUnmap(delete bool) {
// Unmap regions.
for _, r := range f.shm.regions {
for _, r := range s.regions {
r.Unmap()
}
clear(f.shm.regions)
f.shm.regions = f.shm.regions[:0]
clear(s.regions)
s.regions = s.regions[:0]
// Close the file.
if delete && f.shm.File != nil {
os.Remove(f.shm.Name())
if delete && s.File != nil {
os.Remove(s.Name())
}
f.shm.Close()
f.shm.File = nil
s.Close()
s.File = nil
}

View File

@@ -12,6 +12,11 @@ package vfs
// [EXCLUSIVE locking mode]: https://sqlite.org/pragma.html#pragma_locking_mode
const SupportsSharedMemory = false
type vfsShm struct{}
func (vfsShm) Close() error { return nil }
// NewSharedMemory returns a shared-memory WAL-index
// backed a file with the given path.
// It may return nil if shared-memory is not supported,
// or not appropriate for the given flags.
// Only [OPEN_MAIN_DB] databases support WAL mode.
func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
return nil
}