shm_windows.go

  1//go:build (386 || arm || amd64 || arm64 || riscv64 || ppc64le) && !sqlite3_dotlk
  2
  3package vfs
  4
  5import (
  6	"context"
  7	"io"
  8	"os"
  9	"sync"
 10
 11	"github.com/tetratelabs/wazero/api"
 12	"golang.org/x/sys/windows"
 13
 14	"github.com/ncruces/go-sqlite3/internal/util"
 15)
 16
 17type vfsShm struct {
 18	*os.File
 19	mod      api.Module
 20	alloc    api.Function
 21	free     api.Function
 22	path     string
 23	regions  []*util.MappedRegion
 24	shared   [][]byte
 25	shadow   [][_WALINDEX_PGSZ]byte
 26	ptrs     []ptr_t
 27	stack    [1]stk_t
 28	fileLock bool
 29	blocking bool
 30	sync.Mutex
 31}
 32
 33func (s *vfsShm) Close() error {
 34	// Unmap regions.
 35	for _, r := range s.regions {
 36		r.Unmap()
 37	}
 38	s.regions = nil
 39
 40	// Close the file.
 41	return s.File.Close()
 42}
 43
 44func (s *vfsShm) shmOpen() _ErrorCode {
 45	if s.File == nil {
 46		f, err := os.OpenFile(s.path, os.O_RDWR|os.O_CREATE, 0666)
 47		if err != nil {
 48			return _CANTOPEN
 49		}
 50		s.File = f
 51	}
 52	if s.fileLock {
 53		return _OK
 54	}
 55
 56	// Dead man's switch.
 57	if rc := osWriteLock(s.File, _SHM_DMS, 1, 0); rc == _OK {
 58		err := s.Truncate(0)
 59		osUnlock(s.File, _SHM_DMS, 1)
 60		if err != nil {
 61			return _IOERR_SHMOPEN
 62		}
 63	}
 64	rc := osReadLock(s.File, _SHM_DMS, 1, 0)
 65	s.fileLock = rc == _OK
 66	return rc
 67}
 68
 69func (s *vfsShm) shmMap(ctx context.Context, mod api.Module, id, size int32, extend bool) (_ ptr_t, rc _ErrorCode) {
 70	// Ensure size is a multiple of the OS page size.
 71	if size != _WALINDEX_PGSZ || (windows.Getpagesize()-1)&_WALINDEX_PGSZ != 0 {
 72		return 0, _IOERR_SHMMAP
 73	}
 74	if s.mod == nil {
 75		s.mod = mod
 76		s.free = mod.ExportedFunction("sqlite3_free")
 77		s.alloc = mod.ExportedFunction("sqlite3_malloc64")
 78	}
 79	if rc := s.shmOpen(); rc != _OK {
 80		return 0, rc
 81	}
 82
 83	defer s.shmAcquire(&rc)
 84
 85	// Check if file is big enough.
 86	o, err := s.Seek(0, io.SeekEnd)
 87	if err != nil {
 88		return 0, _IOERR_SHMSIZE
 89	}
 90	if n := (int64(id) + 1) * int64(size); n > o {
 91		if !extend {
 92			return 0, _OK
 93		}
 94		if osAllocate(s.File, n) != nil {
 95			return 0, _IOERR_SHMSIZE
 96		}
 97	}
 98
 99	// Maps regions into memory.
100	for int(id) >= len(s.shared) {
101		r, err := util.MapRegion(ctx, mod, s.File, int64(id)*int64(size), size)
102		if err != nil {
103			return 0, _IOERR_SHMMAP
104		}
105		s.regions = append(s.regions, r)
106		s.shared = append(s.shared, r.Data)
107	}
108
109	// Allocate shadow memory.
110	if int(id) >= len(s.shadow) {
111		s.shadow = append(s.shadow, make([][_WALINDEX_PGSZ]byte, int(id)-len(s.shadow)+1)...)
112	}
113
114	// Allocate local memory.
115	for int(id) >= len(s.ptrs) {
116		s.stack[0] = stk_t(size)
117		if err := s.alloc.CallWithStack(ctx, s.stack[:]); err != nil {
118			panic(err)
119		}
120		if s.stack[0] == 0 {
121			panic(util.OOMErr)
122		}
123		clear(util.View(s.mod, ptr_t(s.stack[0]), _WALINDEX_PGSZ))
124		s.ptrs = append(s.ptrs, ptr_t(s.stack[0]))
125	}
126
127	s.shadow[0][4] = 1
128	return s.ptrs[id], _OK
129}
130
131func (s *vfsShm) shmLock(offset, n int32, flags _ShmFlag) (rc _ErrorCode) {
132	switch {
133	case flags&_SHM_LOCK != 0:
134		defer s.shmAcquire(&rc)
135	case flags&_SHM_EXCLUSIVE != 0:
136		s.shmRelease()
137	}
138
139	switch {
140	case flags&_SHM_UNLOCK != 0:
141		return osUnlock(s.File, _SHM_BASE+uint32(offset), uint32(n))
142	case flags&_SHM_SHARED != 0:
143		return osReadLock(s.File, _SHM_BASE+uint32(offset), uint32(n), 0)
144	case flags&_SHM_EXCLUSIVE != 0:
145		return osWriteLock(s.File, _SHM_BASE+uint32(offset), uint32(n), 0)
146	default:
147		panic(util.AssertErr())
148	}
149}
150
151func (s *vfsShm) shmUnmap(delete bool) {
152	if s.File == nil {
153		return
154	}
155
156	s.shmRelease()
157
158	// Free local memory.
159	for _, p := range s.ptrs {
160		s.stack[0] = stk_t(p)
161		if err := s.free.CallWithStack(context.Background(), s.stack[:]); err != nil {
162			panic(err)
163		}
164	}
165	s.ptrs = nil
166	s.shadow = nil
167	s.shared = nil
168
169	// Close the file.
170	s.Close()
171	s.File = nil
172	if delete {
173		os.Remove(s.path)
174	}
175}