From 66dbd422c7bca17e1801e6a198fa7d57db86bbbc Mon Sep 17 00:00:00 2001 From: Nuno Cruces Date: Wed, 30 Oct 2024 00:44:18 +0000 Subject: [PATCH] Share memory by copying. --- driver/example_test.go | 2 +- tests/bradfitz/sql_test.go | 2 +- vfs/adiantum/example_test.go | 2 +- vfs/shm.go | 2 +- vfs/shm_bsd.go | 2 +- vfs/shm_copy.go | 266 +++++++++++++++++++++++++++++++++++ vfs/shm_other.go | 2 +- 7 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 vfs/shm_copy.go diff --git a/driver/example_test.go b/driver/example_test.go index 5628ef94..1e040500 100644 --- a/driver/example_test.go +++ b/driver/example_test.go @@ -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 diff --git a/tests/bradfitz/sql_test.go b/tests/bradfitz/sql_test.go index bc86b0f6..d7e2b3be 100644 --- a/tests/bradfitz/sql_test.go +++ b/tests/bradfitz/sql_test.go @@ -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 diff --git a/vfs/adiantum/example_test.go b/vfs/adiantum/example_test.go index aae7ed96..d4da01a0 100644 --- a/vfs/adiantum/example_test.go +++ b/vfs/adiantum/example_test.go @@ -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 diff --git a/vfs/shm.go b/vfs/shm.go index a63d4141..ed03ae3d 100644 --- a/vfs/shm.go +++ b/vfs/shm.go @@ -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 diff --git a/vfs/shm_bsd.go b/vfs/shm_bsd.go index c5a6aaf3..80f3f15e 100644 --- a/vfs/shm_bsd.go +++ b/vfs/shm_bsd.go @@ -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 diff --git a/vfs/shm_copy.go b/vfs/shm_copy.go new file mode 100644 index 00000000..2f3a4a01 --- /dev/null +++ b/vfs/shm_copy.go @@ -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 + } + } + } +} diff --git a/vfs/shm_other.go b/vfs/shm_other.go index 31d71c4c..f2cedf52 100644 --- a/vfs/shm_other.go +++ b/vfs/shm_other.go @@ -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