diff --git a/vfs/api.go b/vfs/api.go index c8e7fb0..87c9df8 100644 --- a/vfs/api.go +++ b/vfs/api.go @@ -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 } diff --git a/vfs/file.go b/vfs/file.go index d34158f..92170ca 100644 --- a/vfs/file.go +++ b/vfs/file.go @@ -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() } diff --git a/vfs/shm.go b/vfs/shm.go index 67d71b6..bfc1805 100644 --- a/vfs/shm.go +++ b/vfs/shm.go @@ -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 } diff --git a/vfs/shm_other.go b/vfs/shm_other.go index b51dc53..b5c2eed 100644 --- a/vfs/shm_other.go +++ b/vfs/shm_other.go @@ -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 +}