Skip to content

Commit

Permalink
Share memory by copying.
Browse files Browse the repository at this point in the history
  • Loading branch information
ncruces committed Oct 30, 2024
1 parent b2e8636 commit 66dbd42
Show file tree
Hide file tree
Showing 7 changed files with 272 additions and 6 deletions.
2 changes: 1 addition & 1 deletion driver/example_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk

package driver_test

Expand Down
2 changes: 1 addition & 1 deletion tests/bradfitz/sql_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk

package bradfitz

Expand Down
2 changes: 1 addition & 1 deletion vfs/adiantum/example_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && !sqlite3_nosys
//go:build ((linux || darwin || windows || freebsd || openbsd || netbsd || dragonfly || illumos) && !sqlite3_nosys) || sqlite3_flock || sqlite3_dotlk

package adiantum_test

Expand Down
2 changes: 1 addition & 1 deletion 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 || sqlite3_flock) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_noshm || sqlite3_nosys)
//go:build ((darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys)) || sqlite3_flock || sqlite3_dotlk

package vfs

Expand Down
2 changes: 1 addition & 1 deletion vfs/shm_bsd.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build (freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_noshm || sqlite3_nosys)
//go:build ((freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_dotlk || sqlite3_noshm || sqlite3_nosys)) || sqlite3_flock

package vfs

Expand Down
266 changes: 266 additions & 0 deletions vfs/shm_copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
//go:build sqlite3_dotlk

package vfs

import (
"context"
"sync"

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

const _SHM_NLOCK = 8

type vfsShmBuffer struct {
path string
data []byte

refs int

lock [_SHM_NLOCK]int16
sync.Mutex
}

var (
vfsShmBuffers []*vfsShmBuffer
vfsShmBuffersMtx sync.Mutex
)

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
size int32
readOnly bool
}

func (s *vfsShm) Close() error {
if s.vfsShmBuffer == nil {
return nil
}

vfsShmBuffersMtx.Lock()
defer vfsShmBuffersMtx.Unlock()

// Unlock everything.
s.shmLock(0, _SHM_NLOCK, _SHM_UNLOCK)

// Decrease reference count.
if s.vfsShmBuffer.refs > 1 {
s.vfsShmBuffer.refs--
s.vfsShmBuffer = nil
return nil
}

for i, g := range vfsShmBuffers {
if g == s.vfsShmBuffer {
vfsShmBuffers[i] = nil
s.vfsShmBuffer = nil
return nil
}
}
panic(util.AssertErr())
}

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

vfsShmBuffersMtx.Lock()
defer vfsShmBuffersMtx.Unlock()

// Find a shared buffer, increase the reference count.
for _, g := range vfsShmBuffers {
if g != nil && s.path == g.path {
s.vfsShmBuffer = g
g.refs++
return
}
}

// Add the new shared buffer.
s.vfsShmBuffer = &vfsShmBuffer{
path: s.path,
refs: 1,
}
for i, g := range vfsShmBuffers {
if g == nil {
vfsShmBuffers[i] = s.vfsShmBuffer
return
}
}
vfsShmBuffers = append(vfsShmBuffers, s.vfsShmBuffer)
}

func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (uint32, _ErrorCode) {
switch {
case s.size == 0:
s.size = size
s.mod = mod
s.free = mod.ExportedFunction("sqlite3_free")
s.alloc = mod.ExportedFunction("sqlite3_malloc64")
case s.size != size:
return 0, _IOERR_SHMMAP
}

s.shmOpen()
s.Lock()
defer s.Unlock()
defer s.shmAcquire()

n := (int(id) + 1) * int(size)

if n > len(s.data) {
if !extend {
return 0, _OK
}
if s.readOnly {
return 0, _IOERR_SHMSIZE
}
s.data = append(s.data, make([]byte, n-len(s.data))...)
}

if n > len(s.shadow) {
s.shadow = append(s.shadow, make([]byte, n-len(s.shadow))...)
}

if 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]), uint64(s.size)))
s.ptrs = append(s.ptrs, uint32(s.stack[0]))
}

if s.readOnly {
return s.ptrs[id], _READONLY
}
return s.ptrs[id], _OK
}

func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) _ErrorCode {
s.Lock()
defer s.Unlock()

if flags&_SHM_UNLOCK == 0 {
s.shmAcquire()
} else {
s.shmRelease()
}

switch {
case flags&_SHM_UNLOCK != 0:
for i := offset; i < offset+n; i++ {
if s.lock[i] {
if s.vfsShmBuffer.lock[i] == 0 {
panic(util.AssertErr())
}
if s.vfsShmBuffer.lock[i] <= 0 {
s.vfsShmBuffer.lock[i] = 0
} else {
s.vfsShmBuffer.lock[i]--
}
s.lock[i] = false
}
}
case flags&_SHM_SHARED != 0:
for i := offset; i < offset+n; i++ {
if s.lock[i] {
panic(util.AssertErr())
}
if s.vfsShmBuffer.lock[i]+1 <= 0 {
return _BUSY
}
}
for i := offset; i < offset+n; i++ {
s.vfsShmBuffer.lock[i]++
s.lock[i] = true
}
case flags&_SHM_EXCLUSIVE != 0:
for i := offset; i < offset+n; i++ {
if s.lock[i] {
panic(util.AssertErr())
}
if s.vfsShmBuffer.lock[i] != 0 {
return _BUSY
}
}
for i := offset; i < offset+n; i++ {
s.vfsShmBuffer.lock[i] = -1
s.lock[i] = true
}
default:
panic(util.AssertErr())
}

return _OK
}

func (s *vfsShm) shmUnmap(delete bool) {
if s.vfsShmBuffer == nil {
return
}
defer s.Close()

s.Lock()
s.shmRelease()
defer s.Unlock()

for _, p := range s.ptrs {
s.stack[0] = uint64(p)
if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
panic(err)
}
}
}

func (s *vfsShm) shmBarrier() {
s.Lock()
s.shmAcquire()
s.shmRelease()
s.Unlock()
}

func (s *vfsShm) shmAcquire() {
for id, p := range s.ptrs {
start := id * int(s.size)
stop := start + int(s.size)
data := s.data[start:stop]
shadow := s.shadow[start:stop]
local := util.View(s.mod, p, uint64(s.size))
for i, data := range data {
if shadow[i] != data {
shadow[i] = data
local[i] = data
}
}
}
}

func (s *vfsShm) shmRelease() {
for id, p := range s.ptrs {
start := id * int(s.size)
stop := start + int(s.size)
data := s.data[start:stop]
shadow := s.shadow[start:stop]
local := util.View(s.mod, p, uint64(s.size))
for i, local := range local {
if shadow[i] != local {
shadow[i] = local
data[i] = local
}
}
}
}
2 changes: 1 addition & 1 deletion vfs/shm_other.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//go:build !(darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos || sqlite3_flock) || !(386 || arm || amd64 || arm64 || riscv64 || ppc64le) || sqlite3_dotlk || sqlite3_noshm || sqlite3_nosys
//go:build !(((darwin || linux || freebsd || openbsd || netbsd || dragonfly || illumos) && (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !(sqlite3_noshm || sqlite3_nosys)) || sqlite3_flock || sqlite3_dotlk)

package vfs

Expand Down

0 comments on commit 66dbd42

Please sign in to comment.