mirror of https://github.com/mattn/go-sqlite3.git
callback: use handles rather than passing Go pointers
The cgo pointer passing rules forbid passing a Go pointer to C if that pointer points to memory containing other Go pointers. This is true even if the Go pointer is converted to uintptr. This change fixes the code to use a handle instead, and to look up the handle in the callback function.
This commit is contained in:
parent
0cc1174c16
commit
8c66b9cf5e
52
callback.go
52
callback.go
|
@ -24,29 +24,75 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
//export callbackTrampoline
|
//export callbackTrampoline
|
||||||
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
|
func callbackTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
|
||||||
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
|
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
|
||||||
fi := (*functionInfo)(unsafe.Pointer(C.sqlite3_user_data(ctx)))
|
fi := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*functionInfo)
|
||||||
fi.Call(ctx, args)
|
fi.Call(ctx, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export stepTrampoline
|
//export stepTrampoline
|
||||||
func stepTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
|
func stepTrampoline(ctx *C.sqlite3_context, argc int, argv **C.sqlite3_value) {
|
||||||
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
|
args := (*[(math.MaxInt32 - 1) / unsafe.Sizeof((*C.sqlite3_value)(nil))]*C.sqlite3_value)(unsafe.Pointer(argv))[:argc:argc]
|
||||||
ai := (*aggInfo)(unsafe.Pointer(C.sqlite3_user_data(ctx)))
|
ai := lookupHandle(uintptr(C.sqlite3_user_data(ctx))).(*aggInfo)
|
||||||
ai.Step(ctx, args)
|
ai.Step(ctx, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
//export doneTrampoline
|
//export doneTrampoline
|
||||||
func doneTrampoline(ctx *C.sqlite3_context) {
|
func doneTrampoline(ctx *C.sqlite3_context) {
|
||||||
ai := (*aggInfo)(unsafe.Pointer(C.sqlite3_user_data(ctx)))
|
handle := uintptr(C.sqlite3_user_data(ctx))
|
||||||
|
ai := lookupHandle(handle).(*aggInfo)
|
||||||
ai.Done(ctx)
|
ai.Done(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Use handles to avoid passing Go pointers to C.
|
||||||
|
|
||||||
|
type handleVal struct {
|
||||||
|
db *SQLiteConn
|
||||||
|
val interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var handleLock sync.Mutex
|
||||||
|
var handleVals = make(map[uintptr]handleVal)
|
||||||
|
var handleIndex uintptr = 100
|
||||||
|
|
||||||
|
func newHandle(db *SQLiteConn, v interface{}) uintptr {
|
||||||
|
handleLock.Lock()
|
||||||
|
defer handleLock.Unlock()
|
||||||
|
i := handleIndex
|
||||||
|
handleIndex++
|
||||||
|
handleVals[i] = handleVal{db, v}
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func lookupHandle(handle uintptr) interface{} {
|
||||||
|
handleLock.Lock()
|
||||||
|
defer handleLock.Unlock()
|
||||||
|
r, ok := handleVals[handle]
|
||||||
|
if !ok {
|
||||||
|
if handle >= 100 && handle < handleIndex {
|
||||||
|
panic("deleted handle")
|
||||||
|
} else {
|
||||||
|
panic("invalid handle")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r.val
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteHandles(db *SQLiteConn) {
|
||||||
|
handleLock.Lock()
|
||||||
|
defer handleLock.Unlock()
|
||||||
|
for handle, val := range handleVals {
|
||||||
|
if val.db == db {
|
||||||
|
delete(handleVals, handle)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is only here so that tests can refer to it.
|
// This is only here so that tests can refer to it.
|
||||||
type callbackArgRaw C.sqlite3_value
|
type callbackArgRaw C.sqlite3_value
|
||||||
|
|
||||||
|
|
|
@ -367,7 +367,7 @@ func (c *SQLiteConn) RegisterFunc(name string, impl interface{}, pure bool) erro
|
||||||
if pure {
|
if pure {
|
||||||
opts |= C.SQLITE_DETERMINISTIC
|
opts |= C.SQLITE_DETERMINISTIC
|
||||||
}
|
}
|
||||||
rv := C._sqlite3_create_function(c.db, cname, C.int(numArgs), C.int(opts), C.uintptr_t(uintptr(unsafe.Pointer(&fi))), (*[0]byte)(unsafe.Pointer(C.callbackTrampoline)), nil, nil)
|
rv := C._sqlite3_create_function(c.db, cname, C.int(numArgs), C.int(opts), C.uintptr_t(newHandle(c, &fi)), (*[0]byte)(unsafe.Pointer(C.callbackTrampoline)), nil, nil)
|
||||||
if rv != C.SQLITE_OK {
|
if rv != C.SQLITE_OK {
|
||||||
return c.lastError()
|
return c.lastError()
|
||||||
}
|
}
|
||||||
|
@ -492,7 +492,7 @@ func (c *SQLiteConn) RegisterAggregator(name string, impl interface{}, pure bool
|
||||||
if pure {
|
if pure {
|
||||||
opts |= C.SQLITE_DETERMINISTIC
|
opts |= C.SQLITE_DETERMINISTIC
|
||||||
}
|
}
|
||||||
rv := C._sqlite3_create_function(c.db, cname, C.int(stepNArgs), C.int(opts), C.uintptr_t(uintptr(unsafe.Pointer(&ai))), nil, (*[0]byte)(unsafe.Pointer(C.stepTrampoline)), (*[0]byte)(unsafe.Pointer(C.doneTrampoline)))
|
rv := C._sqlite3_create_function(c.db, cname, C.int(stepNArgs), C.int(opts), C.uintptr_t(newHandle(c, &ai)), nil, (*[0]byte)(unsafe.Pointer(C.stepTrampoline)), (*[0]byte)(unsafe.Pointer(C.doneTrampoline)))
|
||||||
if rv != C.SQLITE_OK {
|
if rv != C.SQLITE_OK {
|
||||||
return c.lastError()
|
return c.lastError()
|
||||||
}
|
}
|
||||||
|
@ -705,6 +705,7 @@ func (d *SQLiteDriver) Open(dsn string) (driver.Conn, error) {
|
||||||
|
|
||||||
// Close the connection.
|
// Close the connection.
|
||||||
func (c *SQLiteConn) Close() error {
|
func (c *SQLiteConn) Close() error {
|
||||||
|
deleteHandles(c)
|
||||||
rv := C.sqlite3_close_v2(c.db)
|
rv := C.sqlite3_close_v2(c.db)
|
||||||
if rv != C.SQLITE_OK {
|
if rv != C.SQLITE_OK {
|
||||||
return c.lastError()
|
return c.lastError()
|
||||||
|
|
Loading…
Reference in New Issue