mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 23:52:26 -05:00 
			
		
		
		
	
		
			
	
	
		
			342 lines
		
	
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			342 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) | ||
|  | } |