Skip to content

Commit

Permalink
Recycle memory.
Browse files Browse the repository at this point in the history
  • Loading branch information
ncruces committed Apr 4, 2024
1 parent d0b5407 commit 906c32d
Show file tree
Hide file tree
Showing 8 changed files with 206 additions and 86 deletions.
28 changes: 9 additions & 19 deletions internal/util/handle.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,39 @@ package util
import (
"context"
"io"

"github.com/tetratelabs/wazero/experimental"
)

type handleKey struct{}
type handleState struct {
handles []any
empty int
}

func NewContext(ctx context.Context) context.Context {
state := new(handleState)
ctx = experimental.WithCloseNotifier(ctx, state)
ctx = context.WithValue(ctx, handleKey{}, state)
return ctx
holes int
}

func (s *handleState) CloseNotify(ctx context.Context, exitCode uint32) {
func (s *handleState) closeNotify() {
for _, h := range s.handles {
if c, ok := h.(io.Closer); ok {
c.Close()
}
}
s.handles = nil
s.empty = 0
s.holes = 0
}

func GetHandle(ctx context.Context, id uint32) any {
if id == 0 {
return nil
}
s := ctx.Value(handleKey{}).(*handleState)
s := ctx.Value(moduleKey{}).(*moduleState)
return s.handles[^id]
}

func DelHandle(ctx context.Context, id uint32) error {
if id == 0 {
return nil
}
s := ctx.Value(handleKey{}).(*handleState)
s := ctx.Value(moduleKey{}).(*moduleState)
a := s.handles[^id]
s.handles[^id] = nil
s.empty++
s.holes++
if c, ok := a.(io.Closer); ok {
return c.Close()
}
Expand All @@ -56,13 +46,13 @@ func AddHandle(ctx context.Context, a any) (id uint32) {
if a == nil {
panic(NilErr)
}
s := ctx.Value(handleKey{}).(*handleState)
s := ctx.Value(moduleKey{}).(*moduleState)

// Find an empty slot.
if s.empty > cap(s.handles)-len(s.handles) {
if s.holes > cap(s.handles)-len(s.handles) {
for id, h := range s.handles {
if h == nil {
s.empty--
s.holes--
s.handles[id] = a
return ^uint32(id)
}
Expand Down
106 changes: 106 additions & 0 deletions internal/util/mmap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//go:build (linux || darwin) && (amd64 || arm64) && !sqlite3_flock && !sqlite3_nosys

package util

import (
"context"
"os"
"unsafe"

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

type mmapState struct {
regions []*MappedRegion
}

func (s *mmapState) closeNotify() {
for _, r := range s.regions {
r.Close()
}
s.regions = nil
}

func (s *mmapState) new(ctx context.Context, mod api.Module, size uint32) *MappedRegion {
// Find unused region.
for _, r := range s.regions {
if !r.used && r.size == size {
return r
}
}

// Allocate page aligned memmory.
alloc := mod.ExportedFunction("aligned_alloc")
stack := [2]uint64{
uint64(unix.Getpagesize()),
uint64(size),
}
if err := alloc.CallWithStack(ctx, stack[:]); err != nil {
panic(err)
}
if stack[0] == 0 {
panic(OOMErr)
}

// Save the newly allocated region.
ptr := uint32(stack[0])
buf := View(mod, ptr, uint64(size))
addr := uintptr(unsafe.Pointer(&buf[0]))
s.regions = append(s.regions, &MappedRegion{
Ptr: ptr,
addr: addr,
size: size,
})
return s.regions[len(s.regions)-1]
}

type MappedRegion struct {
addr uintptr
Ptr uint32
size uint32
used bool
}

func MapRegion(ctx context.Context, mod api.Module, f *os.File, offset int64, size uint32) (*MappedRegion, error) {
s := ctx.Value(moduleKey{}).(*moduleState)
r := s.new(ctx, mod, size)
err := r.mmap(f, offset)
if err != nil {
return nil, err
}
return r, nil
}

func (r *MappedRegion) Close() error {
if addr := r.addr; addr != 0 {
r.addr = 0
return munmap(addr, uintptr(r.size))
}
return nil
}

func (r *MappedRegion) Unmap() error {
// We can't munmap the region, otherwise it could be remaped.
// Instead, convert it to a protected, private, anonymous mapping.
// If successful, it can be reused for a subsequent mmap.
_, err := mmap(r.addr, uintptr(r.size),
unix.PROT_NONE, unix.MAP_PRIVATE|unix.MAP_ANON|unix.MAP_FIXED,
-1, 0)
r.used = err != nil
return err
}

func (r *MappedRegion) mmap(f *os.File, offset int64) error {
_, err := mmap(r.addr, uintptr(r.size),
unix.PROT_READ|unix.PROT_WRITE, unix.MAP_SHARED|unix.MAP_FIXED,
int(f.Fd()), offset)
r.used = err == nil
return err
}

//go:linkname mmap syscall.mmap
func mmap(addr, length uintptr, prot, flag, fd int, pos int64) (uintptr, error)

//go:linkname munmap syscall.munmap
func munmap(addr, length uintptr) error
7 changes: 7 additions & 0 deletions internal/util/mmap_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//go:build !(linux || darwin) || !(amd64 || arm64) || sqlite3_flock || sqlite3_nosys

package util

type mmapState struct{}

func (s mmapState) closeNotify() {}
25 changes: 25 additions & 0 deletions internal/util/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package util

import (
"context"

"github.com/tetratelabs/wazero/experimental"
)

type moduleKey struct{}
type moduleState struct {
handleState
mmapState
}

func NewContext(ctx context.Context) context.Context {
state := new(moduleState)
ctx = experimental.WithCloseNotifier(ctx, state)
ctx = context.WithValue(ctx, moduleKey{}, state)
return ctx
}

func (s *moduleState) CloseNotify(ctx context.Context, exitCode uint32) {
s.handleState.closeNotify()
s.mmapState.closeNotify()
}
33 changes: 33 additions & 0 deletions tests/wal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package tests

import (
"path/filepath"
"testing"

"github.com/ncruces/go-sqlite3"
)

func TestWAL_enter_exit(t *testing.T) {
t.Parallel()

file := filepath.Join(t.TempDir(), "test.db")

db, err := sqlite3.Open(file)
if err != nil {
t.Fatal(err)
}
defer db.Close()

err = db.Exec(`
CREATE TABLE test (col);
PRAGMA journal_mode=WAL;
SELECT * FROM test;
PRAGMA journal_mode=DELETE;
SELECT * FROM test;
PRAGMA journal_mode=WAL;
SELECT * FROM test;
`)
if err != nil {
t.Fatal(err)
}
}
2 changes: 1 addition & 1 deletion vfs/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ var (
)

func (f *vfsFile) Close() error {
f.shm.free()
f.shm.Close()
return f.File.Close()
}

Expand Down
Loading

0 comments on commit 906c32d

Please sign in to comment.