aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/modernc.org/sqlite/lib/mutex.go
blob: be44263ae770f712eb36f5ecb27b1eb2704209f3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
// Copyright 2021 The Sqlite Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sqlite3

import (
	"fmt"
	"sync"
	"sync/atomic"
	"unsafe"

	"modernc.org/libc"
	"modernc.org/libc/sys/types"
)

func init() {
	tls := libc.NewTLS()
	if Xsqlite3_threadsafe(tls) == 0 {
		panic(fmt.Errorf("sqlite: thread safety configuration error"))
	}

	varArgs := libc.Xmalloc(tls, types.Size_t(unsafe.Sizeof(uintptr(0))))
	if varArgs == 0 {
		panic(fmt.Errorf("cannot allocate memory"))
	}

	// int sqlite3_config(int, ...);
	if rc := Xsqlite3_config(tls, SQLITE_CONFIG_MUTEX, libc.VaList(varArgs, uintptr(unsafe.Pointer(&mutexMethods)))); rc != SQLITE_OK {
		p := Xsqlite3_errstr(tls, rc)
		str := libc.GoString(p)
		panic(fmt.Errorf("sqlite: failed to configure mutex methods: %v", str))
	}

	libc.Xfree(tls, varArgs)
	tls.Close()
}

var (
	mutexMethods = Sqlite3_mutex_methods{
		FxMutexInit: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS) int32 }{mutexInit})),
		FxMutexEnd:  *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS) int32 }{mutexEnd})),
		FxMutexAlloc: *(*uintptr)(unsafe.Pointer(&struct {
			f func(*libc.TLS, int32) uintptr
		}{mutexAlloc})),
		FxMutexFree:  *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS, uintptr) }{mutexFree})),
		FxMutexEnter: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS, uintptr) }{mutexEnter})),
		FxMutexTry: *(*uintptr)(unsafe.Pointer(&struct {
			f func(*libc.TLS, uintptr) int32
		}{mutexTry})),
		FxMutexLeave: *(*uintptr)(unsafe.Pointer(&struct{ f func(*libc.TLS, uintptr) }{mutexLeave})),
		FxMutexHeld: *(*uintptr)(unsafe.Pointer(&struct {
			f func(*libc.TLS, uintptr) int32
		}{mutexHeld})),
		FxMutexNotheld: *(*uintptr)(unsafe.Pointer(&struct {
			f func(*libc.TLS, uintptr) int32
		}{mutexNotheld})),
	}

	MutexCounters = libc.NewPerfCounter([]string{
		"enter-fast",
		"enter-recursive",
		"enter-recursive-loop",
		"try-fast",
		"try-recursive",
	})
	MutexEnterCallers = libc.NewStackCapture(4)

	mutexes mutexPool

	mutexApp1   = mutexes.alloc(false)
	mutexApp2   = mutexes.alloc(false)
	mutexApp3   = mutexes.alloc(false)
	mutexLRU    = mutexes.alloc(false)
	mutexMaster = mutexes.alloc(false)
	mutexMem    = mutexes.alloc(false)
	mutexOpen   = mutexes.alloc(false)
	mutexPMem   = mutexes.alloc(false)
	mutexPRNG   = mutexes.alloc(false)
	mutexVFS1   = mutexes.alloc(false)
	mutexVFS2   = mutexes.alloc(false)
	mutexVFS3   = mutexes.alloc(false)
)

type mutexPool struct {
	sync.Mutex
	a        []*[256]mutex
	freeList []int
}

func mutexFromPtr(p uintptr) *mutex {
	if p == 0 {
		return nil
	}
	ix := p - 1
	return &mutexes.a[ix>>8][ix&255]
}

func (m *mutexPool) alloc(recursive bool) uintptr {
	m.Lock()

	defer m.Unlock()

	n := len(m.freeList)
	if n == 0 {
		outer := len(m.a) << 8
		m.a = append(m.a, &[256]mutex{})
		for i := 0; i < 256; i++ {
			m.freeList = append(m.freeList, outer+i)
		}
		n = len(m.freeList)
	}
	ix := m.freeList[n-1]
	outer := ix >> 8
	inner := ix & 255
	m.freeList = m.freeList[:n-1]
	p := &m.a[outer][inner]
	p.poolIndex = ix
	p.recursive = recursive
	return uintptr(ix) + 1
}

func (m *mutexPool) free(p uintptr) {
	ptr := mutexFromPtr(p)
	ix := ptr.poolIndex
	*ptr = mutex{}
	m.Lock()

	defer m.Unlock()

	m.freeList = append(m.freeList, ix)
}

type mutex struct {
	sync.Mutex
	wait sync.Mutex

	poolIndex int

	cnt int32
	id  int32

	recursive bool
}

func (m *mutex) enter(id int32) {
	// MutexEnterCallers.Record()
	if !m.recursive {
		// MutexCounters.Inc(0)
		m.Lock()
		m.id = id
		return
	}

	// MutexCounters.Inc(1)
	for {
		m.Lock()
		switch m.id {
		case 0:
			m.cnt = 1
			m.id = id
			m.wait.Lock()
			m.Unlock()
			return
		case id:
			m.cnt++
			m.Unlock()
			return
		}

		// MutexCounters.Inc(2)
		m.Unlock()
		m.wait.Lock()
		//lint:ignore SA2001 TODO report staticcheck issue
		m.wait.Unlock()
	}
}

func (m *mutex) try(id int32) int32 {
	if !m.recursive {
		// MutexCounters.Inc(3)
		return SQLITE_BUSY
	}

	// MutexCounters.Inc(4)
	m.Lock()
	switch m.id {
	case 0:
		m.cnt = 1
		m.id = id
		m.wait.Lock()
		m.Unlock()
		return SQLITE_OK
	case id:
		m.cnt++
		m.Unlock()
		return SQLITE_OK
	}

	m.Unlock()
	return SQLITE_BUSY
}

func (m *mutex) leave(id int32) {
	if !m.recursive {
		m.id = 0
		m.Unlock()
		return
	}

	m.Lock()
	m.cnt--
	if m.cnt == 0 {
		m.id = 0
		m.wait.Unlock()
	}
	m.Unlock()
}

// int (*xMutexInit)(void);
//
// The xMutexInit method defined by this structure is invoked as part of system
// initialization by the sqlite3_initialize() function. The xMutexInit routine
// is called by SQLite exactly once for each effective call to
// sqlite3_initialize().
//
// The xMutexInit() method must be threadsafe. It must be harmless to invoke
// xMutexInit() multiple times within the same process and without intervening
// calls to xMutexEnd(). Second and subsequent calls to xMutexInit() must be
// no-ops. xMutexInit() must not use SQLite memory allocation (sqlite3_malloc()
// and its associates).
//
// If xMutexInit fails in any way, it is expected to clean up after itself
// prior to returning.
func mutexInit(tls *libc.TLS) int32 { return SQLITE_OK }

// int (*xMutexEnd)(void);
func mutexEnd(tls *libc.TLS) int32 { return SQLITE_OK }

// sqlite3_mutex *(*xMutexAlloc)(int);
//
// The sqlite3_mutex_alloc() routine allocates a new mutex and returns a
// pointer to it. The sqlite3_mutex_alloc() routine returns NULL if it is
// unable to allocate the requested mutex. The argument to
// sqlite3_mutex_alloc() must one of these integer constants:
//
//	SQLITE_MUTEX_FAST
//	SQLITE_MUTEX_RECURSIVE
//	SQLITE_MUTEX_STATIC_MASTER
//	SQLITE_MUTEX_STATIC_MEM
//	SQLITE_MUTEX_STATIC_OPEN
//	SQLITE_MUTEX_STATIC_PRNG
//	SQLITE_MUTEX_STATIC_LRU
//	SQLITE_MUTEX_STATIC_PMEM
//	SQLITE_MUTEX_STATIC_APP1
//	SQLITE_MUTEX_STATIC_APP2
//	SQLITE_MUTEX_STATIC_APP3
//	SQLITE_MUTEX_STATIC_VFS1
//	SQLITE_MUTEX_STATIC_VFS2
//	SQLITE_MUTEX_STATIC_VFS3
//
// The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) cause
// sqlite3_mutex_alloc() to create a new mutex. The new mutex is recursive when
// SQLITE_MUTEX_RECURSIVE is used but not necessarily so when SQLITE_MUTEX_FAST
// is used. The mutex implementation does not need to make a distinction
// between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does not want to.
// SQLite will only request a recursive mutex in cases where it really needs
// one. If a faster non-recursive mutex implementation is available on the host
// platform, the mutex subsystem might return such a mutex in response to
// SQLITE_MUTEX_FAST.
//
// The other allowed parameters to sqlite3_mutex_alloc() (anything other than
// SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return a pointer to a
// static preexisting mutex. Nine static mutexes are used by the current
// version of SQLite. Future versions of SQLite may add additional static
// mutexes. Static mutexes are for internal use by SQLite only. Applications
// that use SQLite mutexes should use only the dynamic mutexes returned by
// SQLITE_MUTEX_FAST or SQLITE_MUTEX_RECURSIVE.
//
// Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST or
// SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() returns a
// different mutex on every call. For the static mutex types, the same mutex is
// returned on every call that has the same type number.
func mutexAlloc(tls *libc.TLS, typ int32) uintptr {
	defer func() {
	}()
	switch typ {
	case SQLITE_MUTEX_FAST:
		return mutexes.alloc(false)
	case SQLITE_MUTEX_RECURSIVE:
		return mutexes.alloc(true)
	case SQLITE_MUTEX_STATIC_MASTER:
		return mutexMaster
	case SQLITE_MUTEX_STATIC_MEM:
		return mutexMem
	case SQLITE_MUTEX_STATIC_OPEN:
		return mutexOpen
	case SQLITE_MUTEX_STATIC_PRNG:
		return mutexPRNG
	case SQLITE_MUTEX_STATIC_LRU:
		return mutexLRU
	case SQLITE_MUTEX_STATIC_PMEM:
		return mutexPMem
	case SQLITE_MUTEX_STATIC_APP1:
		return mutexApp1
	case SQLITE_MUTEX_STATIC_APP2:
		return mutexApp2
	case SQLITE_MUTEX_STATIC_APP3:
		return mutexApp3
	case SQLITE_MUTEX_STATIC_VFS1:
		return mutexVFS1
	case SQLITE_MUTEX_STATIC_VFS2:
		return mutexVFS2
	case SQLITE_MUTEX_STATIC_VFS3:
		return mutexVFS3
	default:
		return 0
	}
}

// void (*xMutexFree)(sqlite3_mutex *);
func mutexFree(tls *libc.TLS, m uintptr) { mutexes.free(m) }

// The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt to enter
// a mutex. If another thread is already within the mutex,
// sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return
// SQLITE_BUSY. The sqlite3_mutex_try() interface returns SQLITE_OK upon
// successful entry. Mutexes created using SQLITE_MUTEX_RECURSIVE can be
// entered multiple times by the same thread. In such cases, the mutex must be
// exited an equal number of times before another thread can enter. If the same
// thread tries to enter any mutex other than an SQLITE_MUTEX_RECURSIVE more
// than once, the behavior is undefined.
//
// If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or
// sqlite3_mutex_leave() is a NULL pointer, then all three routines behave as
// no-ops.

// void (*xMutexEnter)(sqlite3_mutex *);
func mutexEnter(tls *libc.TLS, m uintptr) {
	if m == 0 {
		return
	}
	mutexFromPtr(m).enter(tls.ID)
}

// int (*xMutexTry)(sqlite3_mutex *);
func mutexTry(tls *libc.TLS, m uintptr) int32 {
	if m == 0 {
		return SQLITE_OK
	}

	return mutexFromPtr(m).try(tls.ID)
}

// void (*xMutexLeave)(sqlite3_mutex *);
func mutexLeave(tls *libc.TLS, m uintptr) {
	if m == 0 {
		return
	}

	mutexFromPtr(m).leave(tls.ID)
}

// The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines are intended
// for use inside assert() statements. The SQLite core never uses these
// routines except inside an assert() and applications are advised to follow
// the lead of the core. The SQLite core only provides implementations for
// these routines when it is compiled with the SQLITE_DEBUG flag. External
// mutex implementations are only required to provide these routines if
// SQLITE_DEBUG is defined and if NDEBUG is not defined.
//
// These routines should return true if the mutex in their argument is held or
// not held, respectively, by the calling thread.
//
// The implementation is not required to provide versions of these routines
// that actually work. If the implementation does not provide working versions
// of these routines, it should at least provide stubs that always return true
// so that one does not get spurious assertion failures.
//
// If the argument to sqlite3_mutex_held() is a NULL pointer then the routine
// should return 1. This seems counter-intuitive since clearly the mutex cannot
// be held if it does not exist. But the reason the mutex does not exist is
// because the build is not using mutexes. And we do not want the assert()
// containing the call to sqlite3_mutex_held() to fail, so a non-zero return is
// the appropriate thing to do. The sqlite3_mutex_notheld() interface should
// also return 1 when given a NULL pointer.

// int (*xMutexHeld)(sqlite3_mutex *);
func mutexHeld(tls *libc.TLS, m uintptr) int32 {
	if m == 0 {
		return 1
	}

	return libc.Bool32(atomic.LoadInt32(&mutexFromPtr(m).id) == tls.ID)
}

// int (*xMutexNotheld)(sqlite3_mutex *);
func mutexNotheld(tls *libc.TLS, m uintptr) int32 {
	if m == 0 {
		return 1
	}

	return libc.Bool32(atomic.LoadInt32(&mutexFromPtr(m).id) != tls.ID)
}