// 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 (
	
	
	
	
	

	
	
)

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

	 := libc.Xmalloc(, types.Size_t(unsafe.Sizeof(uintptr(0))))
	if  == 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  := Xsqlite3_config(, SQLITE_CONFIG_MUTEX, libc.VaList(, uintptr(unsafe.Pointer(&mutexMethods))));  != SQLITE_OK {
			 := Xsqlite3_errstr(, )
			 := libc.GoString()
			panic(fmt.Errorf("sqlite: failed to configure mutex methods: %v", ))
		}
	}

	libc.Xfree(, )
	.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 ( *mutex) ( int32) {
	if !.recursive {
		.Lock()
		.id = 
		return
	}

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

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

func ( *mutex) ( int32) int32 {
	if !.recursive {
		return SQLITE_BUSY
	}

	.Lock()
	switch .id {
	case 0:
		.cnt = 1
		.id = 
		.wait.Lock()
		.Unlock()
		return SQLITE_OK
	case :
		.cnt++
		.Unlock()
		return SQLITE_OK
	}

	.Unlock()
	return SQLITE_BUSY
}

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

	.Lock()
	.cnt--
	if .cnt == 0 {
		.id = 0
		.wait.Unlock()
	}
	.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 ( *libc.TLS) int32 { return SQLITE_OK }

// int (*xMutexEnd)(void);
func ( *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 ( *libc.TLS,  int32) uintptr {
	defer func() {
	}()
	switch  {
	case SQLITE_MUTEX_FAST:
		return libc.Xcalloc(, 1, types.Size_t(unsafe.Sizeof(mutex{})))
	case SQLITE_MUTEX_RECURSIVE:
		 := libc.Xcalloc(, 1, types.Size_t(unsafe.Sizeof(mutex{})))
		(*mutex)(unsafe.Pointer()).recursive = true
		return 
	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 ( *libc.TLS,  uintptr) { libc.Xfree(, ) }

// 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 ( *libc.TLS,  uintptr) {
	if  == 0 {
		return
	}

	(*mutex)(unsafe.Pointer()).enter(.ID)
}

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

	return (*mutex)(unsafe.Pointer()).try(.ID)
}

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

	(*mutex)(unsafe.Pointer()).leave(.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 ( *libc.TLS,  uintptr) int32 {
	if  == 0 {
		return 1
	}

	return libc.Bool32(atomic.LoadInt32(&(*mutex)(unsafe.Pointer()).id) == .ID)
}

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

	return libc.Bool32(atomic.LoadInt32(&(*mutex)(unsafe.Pointer()).id) != .ID)
}