mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 06:12:25 -05:00 
			
		
		
		
	* Add SQLite support, fix un-thread-safe DB caches, small performance fixes Signed-off-by: kim (grufwub) <grufwub@gmail.com> * add SQLite licenses to README Signed-off-by: kim (grufwub) <grufwub@gmail.com> * appease the linter, and fix my dumbass-ery Signed-off-by: kim (grufwub) <grufwub@gmail.com> * make requested changes Signed-off-by: kim (grufwub) <grufwub@gmail.com> * add back comment Signed-off-by: kim (grufwub) <grufwub@gmail.com>
		
			
				
	
	
		
			341 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // 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"
 | |
| 	"runtime"
 | |
| 	"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"))
 | |
| 	}
 | |
| 
 | |
| 	// experimental pthreads support currently only on linux/amd64
 | |
| 	if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
 | |
| 		// 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})),
 | |
| 	}
 | |
| 
 | |
| 	mutexApp1   mutex
 | |
| 	mutexApp2   mutex
 | |
| 	mutexApp3   mutex
 | |
| 	mutexLRU    mutex
 | |
| 	mutexMaster mutex
 | |
| 	mutexMem    mutex
 | |
| 	mutexOpen   mutex
 | |
| 	mutexPMem   mutex
 | |
| 	mutexPRNG   mutex
 | |
| 	mutexVFS1   mutex
 | |
| 	mutexVFS2   mutex
 | |
| 	mutexVFS3   mutex
 | |
| )
 | |
| 
 | |
| type mutex struct {
 | |
| 	cnt int32
 | |
| 	id  int32
 | |
| 	sync.Mutex
 | |
| 	wait      sync.Mutex
 | |
| 	recursive bool
 | |
| }
 | |
| 
 | |
| func (m *mutex) enter(id int32) {
 | |
| 	if !m.recursive {
 | |
| 		m.Lock()
 | |
| 		m.id = id
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	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
 | |
| 		}
 | |
| 
 | |
| 		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 {
 | |
| 		return SQLITE_BUSY
 | |
| 	}
 | |
| 
 | |
| 	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 libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{})))
 | |
| 	case SQLITE_MUTEX_RECURSIVE:
 | |
| 		p := libc.Xcalloc(tls, 1, types.Size_t(unsafe.Sizeof(mutex{})))
 | |
| 		(*mutex)(unsafe.Pointer(p)).recursive = true
 | |
| 		return p
 | |
| 	case SQLITE_MUTEX_STATIC_MASTER:
 | |
| 		return uintptr(unsafe.Pointer(&mutexMaster))
 | |
| 	case SQLITE_MUTEX_STATIC_MEM:
 | |
| 		return uintptr(unsafe.Pointer(&mutexMem))
 | |
| 	case SQLITE_MUTEX_STATIC_OPEN:
 | |
| 		return uintptr(unsafe.Pointer(&mutexOpen))
 | |
| 	case SQLITE_MUTEX_STATIC_PRNG:
 | |
| 		return uintptr(unsafe.Pointer(&mutexPRNG))
 | |
| 	case SQLITE_MUTEX_STATIC_LRU:
 | |
| 		return uintptr(unsafe.Pointer(&mutexLRU))
 | |
| 	case SQLITE_MUTEX_STATIC_PMEM:
 | |
| 		return uintptr(unsafe.Pointer(&mutexPMem))
 | |
| 	case SQLITE_MUTEX_STATIC_APP1:
 | |
| 		return uintptr(unsafe.Pointer(&mutexApp1))
 | |
| 	case SQLITE_MUTEX_STATIC_APP2:
 | |
| 		return uintptr(unsafe.Pointer(&mutexApp2))
 | |
| 	case SQLITE_MUTEX_STATIC_APP3:
 | |
| 		return uintptr(unsafe.Pointer(&mutexApp3))
 | |
| 	case SQLITE_MUTEX_STATIC_VFS1:
 | |
| 		return uintptr(unsafe.Pointer(&mutexVFS1))
 | |
| 	case SQLITE_MUTEX_STATIC_VFS2:
 | |
| 		return uintptr(unsafe.Pointer(&mutexVFS2))
 | |
| 	case SQLITE_MUTEX_STATIC_VFS3:
 | |
| 		return uintptr(unsafe.Pointer(&mutexVFS3))
 | |
| 	default:
 | |
| 		return 0
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // void (*xMutexFree)(sqlite3_mutex *);
 | |
| func mutexFree(tls *libc.TLS, m uintptr) { libc.Xfree(tls, 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
 | |
| 	}
 | |
| 
 | |
| 	(*mutex)(unsafe.Pointer(m)).enter(tls.ID)
 | |
| }
 | |
| 
 | |
| // int (*xMutexTry)(sqlite3_mutex *);
 | |
| func mutexTry(tls *libc.TLS, m uintptr) int32 {
 | |
| 	if m == 0 {
 | |
| 		return SQLITE_OK
 | |
| 	}
 | |
| 
 | |
| 	return (*mutex)(unsafe.Pointer(m)).try(tls.ID)
 | |
| }
 | |
| 
 | |
| // void (*xMutexLeave)(sqlite3_mutex *);
 | |
| func mutexLeave(tls *libc.TLS, m uintptr) {
 | |
| 	if m == 0 {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	(*mutex)(unsafe.Pointer(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(&(*mutex)(unsafe.Pointer(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(&(*mutex)(unsafe.Pointer(m)).id) != tls.ID)
 | |
| }
 |