mirror of
https://github.com/ncruces/go-sqlite3.git
synced 2026-01-12 05:59:14 +00:00
Windows shared memory. (#181)
This commit is contained in:
4
.github/workflows/build-test.sh
vendored
4
.github/workflows/build-test.sh
vendored
@@ -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
.github/workflows/test.yml
vendored
11
.github/workflows/test.yml
vendored
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
|
||||
53
internal/util/mmap_windows.go
Normal file
53
internal/util/mmap_windows.go
Normal file
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
@@ -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
|
||||
}
|
||||
@@ -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.
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
@@ -11,8 +14,6 @@ import (
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
)
|
||||
|
||||
const _WALINDEX_PGSZ = 32768
|
||||
|
||||
type vfsShmBuffer struct {
|
||||
shared []byte // +checklocks:Mutex
|
||||
refs int // +checklocks:vfsShmBuffersMtx
|
||||
@@ -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 {
|
||||
@@ -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()
|
||||
@@ -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) {
|
||||
@@ -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()
|
||||
@@ -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()
|
||||
}
|
||||
@@ -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.
|
||||
@@ -1,4 +1,4 @@
|
||||
//go:build (darwin || linux) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys)
|
||||
//go:build (linux || darwin) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_flock || sqlite3_dotlk || sqlite3_nosys)
|
||||
|
||||
package vfs
|
||||
|
||||
@@ -21,21 +21,20 @@ type vfsShm struct {
|
||||
regions []*util.MappedRegion
|
||||
readOnly bool
|
||||
blocking bool
|
||||
barrier sync.Mutex
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
var _ blockingSharedMemory = &vfsShm{}
|
||||
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
var flag int
|
||||
if s.readOnly {
|
||||
flag = unix.O_RDONLY
|
||||
} else {
|
||||
flag = unix.O_RDWR
|
||||
}
|
||||
f, err := os.OpenFile(s.path,
|
||||
flag|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
unix.O_RDWR|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
if err != nil {
|
||||
f, err = os.OpenFile(s.path,
|
||||
unix.O_RDONLY|unix.O_CREAT|unix.O_NOFOLLOW, 0666)
|
||||
s.readOnly = true
|
||||
}
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
@@ -65,10 +64,7 @@ func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
return _IOERR_SHMOPEN
|
||||
}
|
||||
}
|
||||
if rc := osReadLock(s.File, _SHM_DMS, 1, time.Millisecond); rc != _OK {
|
||||
return rc
|
||||
}
|
||||
return _OK
|
||||
return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
|
||||
@@ -95,13 +91,7 @@ func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, ext
|
||||
}
|
||||
}
|
||||
|
||||
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, s.readOnly)
|
||||
if err != nil {
|
||||
return 0, _IOERR_SHMMAP
|
||||
}
|
||||
@@ -157,8 +147,7 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
||||
for _, r := range s.regions {
|
||||
r.Unmap()
|
||||
}
|
||||
clear(s.regions)
|
||||
s.regions = s.regions[:0]
|
||||
s.regions = nil
|
||||
|
||||
// Close the file.
|
||||
if delete {
|
||||
@@ -169,9 +158,9 @@ func (s *vfsShm) shmUnmap(delete bool) {
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmBarrier() {
|
||||
s.barrier.Lock()
|
||||
s.Lock()
|
||||
//lint:ignore SA2001 memory barrier.
|
||||
s.barrier.Unlock()
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmEnableBlocking(block bool) {
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
242
vfs/shm_windows.go
Normal file
242
vfs/shm_windows.go
Normal file
@@ -0,0 +1,242 @@
|
||||
//go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_nosys)
|
||||
|
||||
package vfs
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
"unsafe"
|
||||
|
||||
"github.com/tetratelabs/wazero/api"
|
||||
"golang.org/x/sys/windows"
|
||||
|
||||
"github.com/ncruces/go-sqlite3/internal/util"
|
||||
"github.com/ncruces/go-sqlite3/util/osutil"
|
||||
)
|
||||
|
||||
type vfsShm struct {
|
||||
*os.File
|
||||
mod api.Module
|
||||
alloc api.Function
|
||||
free api.Function
|
||||
path string
|
||||
regions []*util.MappedRegion
|
||||
shared [][]byte
|
||||
shadow []byte
|
||||
ptrs []uint32
|
||||
stack [1]uint64
|
||||
blocking bool
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
var _ blockingSharedMemory = &vfsShm{}
|
||||
|
||||
func (s *vfsShm) Close() error {
|
||||
// Unmap regions.
|
||||
for _, r := range s.regions {
|
||||
r.Unmap()
|
||||
}
|
||||
s.regions = nil
|
||||
|
||||
// Close the file.
|
||||
return s.File.Close()
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmOpen() _ErrorCode {
|
||||
if s.File == nil {
|
||||
f, err := osutil.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
|
||||
if err != nil {
|
||||
return _CANTOPEN
|
||||
}
|
||||
s.File = f
|
||||
}
|
||||
|
||||
// Dead man's switch.
|
||||
if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
|
||||
err := s.Truncate(0)
|
||||
osUnlock(s.File, _SHM_DMS, 1)
|
||||
if err != nil {
|
||||
return _IOERR_SHMOPEN
|
||||
}
|
||||
}
|
||||
return osReadLock(s.File, _SHM_DMS, 1, time.Millisecond)
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
|
||||
// Ensure size is a multiple of the OS page size.
|
||||
if size != _WALINDEX_PGSZ || (windows.Getpagesize()-1)&_WALINDEX_PGSZ != 0 {
|
||||
return 0, _IOERR_SHMMAP
|
||||
}
|
||||
if s.mod == nil {
|
||||
s.mod = mod
|
||||
s.free = mod.ExportedFunction("sqlite3_free")
|
||||
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
|
||||
}
|
||||
if rc := s.shmOpen(); rc != _OK {
|
||||
return 0, rc
|
||||
}
|
||||
|
||||
defer s.shmAcquire()
|
||||
|
||||
// Check if file is big enough.
|
||||
o, err := s.Seek(0, io.SeekEnd)
|
||||
if err != nil {
|
||||
return 0, _IOERR_SHMSIZE
|
||||
}
|
||||
if n := (int64(id) + 1) * int64(size); n > o {
|
||||
if !extend {
|
||||
return 0, _OK
|
||||
}
|
||||
if osAllocate(s.File, n) != nil {
|
||||
return 0, _IOERR_SHMSIZE
|
||||
}
|
||||
}
|
||||
|
||||
// Map the region into memory.
|
||||
r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size)
|
||||
if err != nil {
|
||||
return 0, _IOERR_SHMMAP
|
||||
}
|
||||
s.regions = append(s.regions, r)
|
||||
|
||||
if int(id) >= len(s.shared) {
|
||||
s.shared = append(s.shared, make([][]byte, int(id)-len(s.shared)+1)...)
|
||||
}
|
||||
s.shared[id] = r.Data
|
||||
|
||||
// Allocate shadow memory.
|
||||
if n := (int(id) + 1) * int(size); n > len(s.shadow) {
|
||||
s.shadow = append(s.shadow, make([]byte, n-len(s.shadow))...)
|
||||
}
|
||||
|
||||
// Allocate local memory.
|
||||
for int(id) >= len(s.ptrs) {
|
||||
s.stack[0] = uint64(size)
|
||||
if err := s.alloc.CallWithStack(ctx, s.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if s.stack[0] == 0 {
|
||||
panic(util.OOMErr)
|
||||
}
|
||||
clear(util.View(s.mod, uint32(s.stack[0]), _WALINDEX_PGSZ))
|
||||
s.ptrs = append(s.ptrs, uint32(s.stack[0]))
|
||||
}
|
||||
|
||||
return s.ptrs[id], _OK
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
|
||||
switch {
|
||||
case flags&_SHM_LOCK != 0:
|
||||
defer s.shmAcquire()
|
||||
case flags&_SHM_EXCLUSIVE != 0:
|
||||
s.shmRelease()
|
||||
}
|
||||
|
||||
var timeout time.Duration
|
||||
if s.blocking {
|
||||
timeout = time.Millisecond
|
||||
}
|
||||
|
||||
switch {
|
||||
case flags&_SHM_UNLOCK != 0:
|
||||
return osUnlock(s.File, _SHM_BASE+uint32(offset), uint32(n))
|
||||
case flags&_SHM_SHARED != 0:
|
||||
return osReadLock(s.File, _SHM_BASE+uint32(offset), uint32(n), timeout)
|
||||
case flags&_SHM_EXCLUSIVE != 0:
|
||||
return osWriteLock(s.File, _SHM_BASE+uint32(offset), uint32(n), timeout)
|
||||
default:
|
||||
panic(util.AssertErr())
|
||||
}
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmUnmap(delete bool) {
|
||||
if s.File == nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.shmRelease()
|
||||
|
||||
// Free local memory.
|
||||
for _, p := range s.ptrs {
|
||||
s.stack[0] = uint64(p)
|
||||
if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
s.ptrs = nil
|
||||
s.shadow = nil
|
||||
s.shared = nil
|
||||
|
||||
// Close the file.
|
||||
s.Close()
|
||||
s.File = nil
|
||||
if delete {
|
||||
os.Remove(s.path)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmBarrier() {
|
||||
s.Lock()
|
||||
s.shmAcquire()
|
||||
s.shmRelease()
|
||||
s.Unlock()
|
||||
}
|
||||
|
||||
const _WALINDEX_PGSZ = 32768
|
||||
|
||||
func (s *vfsShm) shmAcquire() {
|
||||
// Copies modified words from shared to private memory.
|
||||
for id, p := range s.ptrs {
|
||||
i0 := id * _WALINDEX_PGSZ
|
||||
i1 := i0 + _WALINDEX_PGSZ
|
||||
shared := shmPage(s.shared[id])
|
||||
shadow := shmPage(s.shadow[i0:i1])
|
||||
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
|
||||
if shmPageEq(shadow, shared) {
|
||||
continue
|
||||
}
|
||||
for i, shared := range shared {
|
||||
if shadow[i] != shared {
|
||||
shadow[i] = shared
|
||||
privat[i] = shared
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmRelease() {
|
||||
// Copies modified words from private to shared memory.
|
||||
for id, p := range s.ptrs {
|
||||
i0 := id * _WALINDEX_PGSZ
|
||||
i1 := i0 + _WALINDEX_PGSZ
|
||||
shared := shmPage(s.shared[id])
|
||||
shadow := shmPage(s.shadow[i0:i1])
|
||||
privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
|
||||
if shmPageEq(shadow, privat) {
|
||||
continue
|
||||
}
|
||||
for i, privat := range privat {
|
||||
if shadow[i] != privat {
|
||||
shadow[i] = privat
|
||||
shared[i] = privat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
|
||||
p := (*uint32)(unsafe.Pointer(unsafe.SliceData(s)))
|
||||
return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
|
||||
}
|
||||
|
||||
func shmPageEq(p1, p2 *[_WALINDEX_PGSZ / 4]uint32) bool {
|
||||
return *(*[_WALINDEX_PGSZ / 8]uint32)(p1[:]) == *(*[_WALINDEX_PGSZ / 8]uint32)(p2[:])
|
||||
}
|
||||
|
||||
func (s *vfsShm) shmEnableBlocking(block bool) {
|
||||
s.blocking = block
|
||||
}
|
||||
@@ -145,9 +145,6 @@ func Test_crash01(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
if os.Getenv("CI") != "" {
|
||||
t.Skip("skipping in CI")
|
||||
}
|
||||
if !vfs.SupportsFileLocking {
|
||||
t.Skip("skipping without locks")
|
||||
}
|
||||
@@ -212,9 +209,6 @@ func Test_crash01_wal(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping in short mode")
|
||||
}
|
||||
if os.Getenv("CI") != "" {
|
||||
t.Skip("skipping in CI")
|
||||
}
|
||||
if !vfs.SupportsSharedMemory {
|
||||
t.Skip("skipping without shared memory")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user