1//go:build (windows && (386 || arm || amd64 || arm64 || riscv64 || ppc64le)) || sqlite3_dotlk
2
3package vfs
4
5import (
6 "unsafe"
7
8 "github.com/ncruces/go-sqlite3/internal/util"
9)
10
11const (
12 _WALINDEX_HDR_SIZE = 136
13 _WALINDEX_PGSZ = 32768
14)
15
16// This seems a safe way of keeping the WAL-index in sync.
17//
18// The WAL-index file starts with a header,
19// and the index doesn't meaningfully change if the header doesn't change.
20//
21// The header starts with two 48 byte, checksummed, copies of the same information,
22// which are accessed independently between memory barriers.
23// The checkpoint information that follows uses 4 byte aligned words.
24//
25// Finally, we have the WAL-index hash tables,
26// which are only modified holding the exclusive WAL_WRITE_LOCK.
27//
28// Since all the data is either redundant+checksummed,
29// 4 byte aligned, or modified under an exclusive lock,
30// the copies below should correctly keep memory in sync.
31//
32// https://sqlite.org/walformat.html#the_wal_index_file_format
33
34func (s *vfsShm) shmAcquire(ptr *_ErrorCode) {
35 if ptr != nil && *ptr != _OK {
36 return
37 }
38 if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], s.shared[0][:]) {
39 return
40 }
41 // Copies modified words from shared to private memory.
42 for id, p := range s.ptrs {
43 shared := shmPage(s.shared[id][:])
44 shadow := shmPage(s.shadow[id][:])
45 privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
46 for i, shared := range shared {
47 if shadow[i] != shared {
48 shadow[i] = shared
49 privat[i] = shared
50 }
51 }
52 }
53}
54
55func (s *vfsShm) shmRelease() {
56 if len(s.ptrs) == 0 || shmEqual(s.shadow[0][:], util.View(s.mod, s.ptrs[0], _WALINDEX_HDR_SIZE)) {
57 return
58 }
59 // Copies modified words from private to shared memory.
60 for id, p := range s.ptrs {
61 shared := shmPage(s.shared[id][:])
62 shadow := shmPage(s.shadow[id][:])
63 privat := shmPage(util.View(s.mod, p, _WALINDEX_PGSZ))
64 for i, privat := range privat {
65 if shadow[i] != privat {
66 shadow[i] = privat
67 shared[i] = privat
68 }
69 }
70 }
71}
72
73func (s *vfsShm) shmBarrier() {
74 s.Lock()
75 s.shmAcquire(nil)
76 s.shmRelease()
77 s.Unlock()
78}
79
80func shmPage(s []byte) *[_WALINDEX_PGSZ / 4]uint32 {
81 p := (*uint32)(unsafe.Pointer(unsafe.SliceData(s)))
82 return (*[_WALINDEX_PGSZ / 4]uint32)(unsafe.Slice(p, _WALINDEX_PGSZ/4))
83}
84
85func shmEqual(v1, v2 []byte) bool {
86 return *(*[_WALINDEX_HDR_SIZE]byte)(v1[:]) == *(*[_WALINDEX_HDR_SIZE]byte)(v2[:])
87}