Skip to content

Commit

Permalink
Windows shared memory. (#181)
Browse files Browse the repository at this point in the history
  • Loading branch information
ncruces authored Nov 5, 2024
1 parent 81e7a94 commit a57ce87
Show file tree
Hide file tree
Showing 12 changed files with 380 additions and 95 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/build-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ echo 'set -eu' > test.sh
for p in $(go list ./...); do
dir=".${p#github.com/ncruces/go-sqlite3}"
name="$(basename "$p").test"
(cd ${dir}; go test -c)
[ -f "${dir}/${name}" ] && echo "(cd ${dir}; ./${name} ${TESTFLAGS})" >> test.sh
(cd ${dir}; go test -c ${BUILDFLAGS:-})
[ -f "${dir}/${name}" ] && echo "(cd ${dir}; ./${name} ${TESTFLAGS:-})" >> test.sh
done
11 changes: 6 additions & 5 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,17 @@ jobs:
- name: Test
run: go test -v ./... -bench . -benchtime=1x

- name: Test no locks
run: go test -v -tags sqlite3_nosys ./...
if: matrix.os == 'ubuntu-latest'

- name: Test BSD locks
run: go test -v -tags sqlite3_flock ./...
if: matrix.os == 'macos-latest'

- name: Test dot locks
run: go test -v -tags sqlite3_dotlk ./...
if: matrix.os == 'macos-latest'

- name: Test no locks
run: go test -v -tags sqlite3_nosys ./...
if: matrix.os == 'ubuntu-latest'
if: matrix.os != 'windows-latest'

- name: Test GORM
shell: bash
Expand Down Expand Up @@ -194,6 +194,7 @@ jobs:
env:
GOOS: solaris
TESTFLAGS: '-test.v -test.short'
BUILDFLAGS: '-tags sqlite3_dotlk'
run: .github/workflows/build-test.sh

- name: Test Solaris
Expand Down
3 changes: 2 additions & 1 deletion internal/testcfg/testcfg.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ func init() {
path := filepath.Join(os.TempDir(), "wazero")
if err := os.MkdirAll(path, 0777); err == nil {
if cache, err := wazero.NewCompilationCacheWithDir(path); err == nil {
sqlite3.RuntimeConfig.WithCompilationCache(cache)
sqlite3.RuntimeConfig = sqlite3.RuntimeConfig.
WithCompilationCache(cache)
}
}
}
Expand Down
10 changes: 7 additions & 3 deletions internal/util/mmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@ type MappedRegion struct {
used bool
}

func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, prot int) (*MappedRegion, error) {
func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32, readOnly bool) (*MappedRegion, error) {
s := ctx.Value(moduleKey{}).(*moduleState)
r := s.new(ctx, mod, size)
err := r.mmap(f, offset, prot)
err := r.mmap(f, offset, readOnly)
if err != nil {
return nil, err
}
Expand All @@ -75,7 +75,11 @@ func (r *MappedRegion) Unmap() error {
return err
}

func (r *MappedRegion) mmap(f *os.File, offset int64, prot int) error {
func (r *MappedRegion) mmap(f *os.File, offset int64, readOnly bool) error {
prot := unix.PROT_READ
if !readOnly {
prot |= unix.PROT_WRITE
}
_, err := unix.MmapPtr(int(f.Fd()), offset, r.addr, uintptr(r.size),
prot, unix.MAP_SHARED|unix.MAP_FIXED)
r.used = err == nil
Expand Down
53 changes: 53 additions & 0 deletions internal/util/mmap_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//go:build !sqlite3_nosys

package util

import (
"context"
"os"
"reflect"
"unsafe"

"github.com/tetratelabs/wazero/api"
"golang.org/x/sys/windows"
)

type MappedRegion struct {
windows.Handle
Data []byte
addr uintptr
}

func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size int32) (*MappedRegion, error) {
h, err := windows.CreateFileMapping(windows.Handle(f.Fd()), nil, windows.PAGE_READWRITE, 0, 0, nil)
if h == 0 {
return nil, err
}

a, err := windows.MapViewOfFile(h, windows.FILE_MAP_WRITE,
uint32(offset>>32), uint32(offset), uintptr(size))
if a == 0 {
windows.CloseHandle(h)
return nil, err
}

res := &MappedRegion{Handle: h, addr: a}
// SliceHeader, although deprecated, avoids a go vet warning.
sh := (*reflect.SliceHeader)(unsafe.Pointer(&res.Data))
sh.Len = int(size)
sh.Cap = int(size)
sh.Data = a
return res, nil
}

func (r *MappedRegion) Unmap() error {
if r.Data == nil {
return nil
}
err := windows.UnmapViewOfFile(r.addr)
if err != nil {
return err
}
r.Data = nil
return windows.CloseHandle(r.Handle)
}
7 changes: 2 additions & 5 deletions vfs/shm.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build ((darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk
//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk

package vfs

Expand All @@ -22,8 +22,5 @@ func NewSharedMemory(path string, flags OpenFlag) SharedMemory {
if flags&OPEN_MAIN_DB == 0 || flags&(OPEN_DELETEONCLOSE|OPEN_MEMORY) != 0 {
return nil
}
return &vfsShm{
path: path,
readOnly: flags&OPEN_READONLY != 0,
}
return &vfsShm{path: path}
}
48 changes: 16 additions & 32 deletions vfs/shm_bsd.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,9 @@ type vfsShmFile struct {
*os.File
info os.FileInfo

// +checklocks:vfsShmFilesMtx
refs int
refs int // +checklocks:vfsShmFilesMtx

// +checklocks:Mutex
lock [_SHM_NLOCK]int16
lock [_SHM_NLOCK]int16 // +checklocks:Mutex
sync.Mutex
}

Expand All @@ -34,10 +32,9 @@ var (

type vfsShm struct {
*vfsShmFile
path string
lock [_SHM_NLOCK]bool
regions []*util.MappedRegion
readOnly bool
path string
lock [_SHM_NLOCK]bool
regions []*util.MappedRegion
}

func (s *vfsShm) Close() error {
Expand Down Expand Up @@ -69,7 +66,7 @@ func (s *vfsShm) Close() error {
panic(util.AssertErr())
}

func (s *vfsShm) shmOpen() (rc _ErrorCode) {
func (s *vfsShm) shmOpen() _ErrorCode {
if s.vfsShmFile != nil {
return _OK
}
Expand Down Expand Up @@ -100,17 +97,13 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
}
}

// Lock and truncate the file, if not readonly.
// Lock and truncate the file.
// The lock is only released by closing the file.
if s.readOnly {
rc = _READONLY_CANTINIT
} else {
if rc := osLock(f, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK); rc != _OK {
return rc
}
if err := f.Truncate(0); err != nil {
return _IOERR_SHMOPEN
}
if rc := osLock(f, unix.LOCK_EX|unix.LOCK_NB, _IOERR_LOCK); rc != _OK {
return rc
}
if err := f.Truncate(0); err != nil {
return _IOERR_SHMOPEN
}

// Add the new shared file.
Expand All @@ -122,11 +115,11 @@ func (s *vfsShm) shmOpen() (rc _ErrorCode) {
for i, g := range vfsShmFiles {
if g == nil {
vfsShmFiles[i] = s.vfsShmFile
return rc
return _OK
}
}
vfsShmFiles = append(vfsShmFiles, s.vfsShmFile)
return rc
return _OK
}

func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
Expand All @@ -148,25 +141,16 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
if !extend {
return 0, _OK
}
if s.readOnly || osAllocate(s.File, n) != nil {
if osAllocate(s.File, n) != nil {
return 0, _IOERR_SHMSIZE
}
}

var prot int
if s.readOnly {
prot = unix.PROT_READ
} else {
prot = unix.PROT_READ | unix.PROT_WRITE
}
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, prot)
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size, false)
if err != nil {
return 0, _IOERR_SHMMAP
}
s.regions = append(s.regions, r)
if s.readOnly {
return r.Ptr, _READONLY
}
return r.Ptr, _OK
}

Expand Down
52 changes: 36 additions & 16 deletions vfs/shm_copy.go → vfs/shm_dotlk.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ package vfs

import (
"context"
"errors"
"io/fs"
"os"
"sync"
"unsafe"

"github.com/ncruces/go-sqlite3/internal/util"
"github.com/tetratelabs/wazero/api"
)

const _WALINDEX_PGSZ = 32768

type vfsShmBuffer struct {
shared []byte // +checklocks:Mutex
refs int // +checklocks:vfsShmBuffersMtx
Expand All @@ -29,15 +30,14 @@ var (

type vfsShm struct {
*vfsShmBuffer
mod api.Module
alloc api.Function
free api.Function
path string
shadow []byte
ptrs []uint32
stack [1]uint64
lock [_SHM_NLOCK]bool
readOnly bool
mod api.Module
alloc api.Function
free api.Function
path string
shadow []byte
ptrs []uint32
stack [1]uint64
lock [_SHM_NLOCK]bool
}

func (s *vfsShm) Close() error {
Expand All @@ -58,13 +58,18 @@ func (s *vfsShm) Close() error {
return nil
}

err := os.Remove(s.path)
if err != nil && !errors.Is(err, fs.ErrNotExist) {
return _IOERR_UNLOCK
}
delete(vfsShmBuffers, s.path)
s.vfsShmBuffer = nil
return nil
}

func (s *vfsShm) shmOpen() {
func (s *vfsShm) shmOpen() _ErrorCode {
if s.vfsShmBuffer != nil {
return
return _OK
}

vfsShmBuffersMtx.Lock()
Expand All @@ -74,12 +79,23 @@ func (s *vfsShm) shmOpen() {
if g, ok := vfsShmBuffers[s.path]; ok {
s.vfsShmBuffer = g
g.refs++
return
return _OK
}

// Create a directory on disk to ensure only this process
// uses this path to register a shared memory.
err := os.Mkdir(s.path, 0777)
if errors.Is(err, fs.ErrExist) {
return _BUSY
}
if err != nil {
return _IOERR_LOCK
}

// Add the new shared buffer.
s.vfsShmBuffer = &vfsShmBuffer{}
vfsShmBuffers[s.path] = s.vfsShmBuffer
return _OK
}

func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
Expand All @@ -91,8 +107,10 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
s.free = mod.ExportedFunction("sqlite3_free")
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
}
if rc := s.shmOpen(); rc != _OK {
return 0, rc
}

s.shmOpen()
s.Lock()
defer s.Unlock()
defer s.shmAcquire()
Expand Down Expand Up @@ -131,7 +149,7 @@ func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {

switch {
case flags&_SHM_LOCK != 0:
s.shmAcquire()
defer s.shmAcquire()
case flags&_SHM_EXCLUSIVE != 0:
s.shmRelease()
}
Expand Down Expand Up @@ -228,6 +246,8 @@ func (s *vfsShm) shmBarrier() {
//
// https://sqlite.org/walformat.html#the_wal_index_file_format

const _WALINDEX_PGSZ = 32768

// +checklocks:s.Mutex
func (s *vfsShm) shmAcquire() {
// Copies modified words from shared to private memory.
Expand Down
Loading

0 comments on commit a57ce87

Please sign in to comment.