Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ build:
go build ./...

test:
go test -v ./...
go test -race ./kernel -run TestTransactionHandlePtrRaceDetector
go test -race -v ./...

clean:
rm -rf depend/bitcoin/build
Expand Down
37 changes: 37 additions & 0 deletions kernel/cgo_handle.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package kernel

/*
#include <stdlib.h>
*/
import "C"
import (
"runtime/cgo"
"unsafe"
)

func newCgoHandlePointer(value any) unsafe.Pointer {
userData := C.malloc(C.size_t(unsafe.Sizeof(cgo.Handle(0))))
if userData == nil {
panic("Failed to allocate callback handle storage")
}
handlePtr := (*cgo.Handle)(userData)
*handlePtr = cgo.NewHandle(value)
return userData
}

func cgoHandleFromPointer(userData unsafe.Pointer) cgo.Handle {
handlePtr := (*cgo.Handle)(userData)
return *handlePtr
}

func deleteCgoHandlePointer(userData unsafe.Pointer) {
if userData != nil {
cgoHandleFromPointer(userData).Delete()
C.free(userData)
}
}

//export go_delete_handle
func go_delete_handle(userData unsafe.Pointer) {
deleteCgoHandlePointer(userData)
}
6 changes: 0 additions & 6 deletions kernel/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package kernel
import "C"
import (
"runtime"
"runtime/cgo"
"unsafe"
)

Expand Down Expand Up @@ -88,11 +87,6 @@ func (h *handle) Destroy() {
h.destroy()
}

//export go_delete_handle
func go_delete_handle(handle unsafe.Pointer) {
cgo.Handle(handle).Delete()
}

func ReverseBytes(data []byte) []byte {
result := make([]byte, len(data))
for i, b := range data {
Expand Down
12 changes: 4 additions & 8 deletions kernel/context_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ package kernel
#include "bitcoinkernel.h"
#include <stdint.h>

// Bridge functions: exported Go functions that C library can call
// user_data contains the cgo.Handle ID as void* for callback identification
// Bridge functions exported from Go for kernel callbacks.
// user_data points to cgo.Handle storage.
extern void go_notify_block_tip_bridge(void* user_data, btck_SynchronizationState state, btck_BlockTreeEntry* entry, double verification_progress);
extern void go_notify_header_tip_bridge(void* user_data, btck_SynchronizationState state, int64_t height, int64_t timestamp, int presync);
extern void go_notify_progress_bridge(void* user_data, const char* title, size_t title_len, int progress_percent, int resume_possible);
Expand All @@ -21,10 +21,6 @@ extern void go_validation_interface_block_disconnected_bridge(void* user_data, b
extern void go_delete_handle(void* user_data);
*/
import "C"
import (
"runtime/cgo"
"unsafe"
)

// ContextOption is a functional option for configuring context options.
type ContextOption func(*C.btck_ContextOptions) error
Expand Down Expand Up @@ -54,7 +50,7 @@ func WithChainType(chainType ChainType) ContextOption {
func WithNotifications(callbacks *NotificationCallbacks) ContextOption {
return func(opts *C.btck_ContextOptions) error {
notificationCallbacks := C.btck_NotificationInterfaceCallbacks{
user_data: unsafe.Pointer(cgo.NewHandle(callbacks)),
user_data: newCgoHandlePointer(callbacks),
user_data_destroy: C.btck_DestroyCallback(C.go_delete_handle),
block_tip: C.btck_NotifyBlockTip(C.go_notify_block_tip_bridge),
header_tip: C.btck_NotifyHeaderTip(C.go_notify_header_tip_bridge),
Expand All @@ -78,7 +74,7 @@ func WithNotifications(callbacks *NotificationCallbacks) ContextOption {
func WithValidationInterface(callbacks *ValidationInterfaceCallbacks) ContextOption {
return func(opts *C.btck_ContextOptions) error {
validationCallbacks := C.btck_ValidationInterfaceCallbacks{
user_data: unsafe.Pointer(cgo.NewHandle(callbacks)),
user_data: newCgoHandlePointer(callbacks),
user_data_destroy: C.btck_DestroyCallback(C.go_delete_handle),
block_checked: C.btck_ValidationInterfaceBlockChecked(C.go_validation_interface_block_checked_bridge),
pow_valid_block: C.btck_ValidationInterfacePoWValidBlock(C.go_validation_interface_pow_valid_block_bridge),
Expand Down
14 changes: 6 additions & 8 deletions kernel/logging_connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,14 @@ package kernel
#include <stdlib.h>
#include <stdint.h>

// Bridge function: exported Go function that C library can call
// user_data contains the cgo.Handle ID as void* for callback identification
// Bridge function exported from Go for kernel log callbacks.
// user_data points to cgo.Handle storage.
extern void go_log_callback_bridge(void* user_data, char* message, size_t message_len);

extern void go_delete_handle(void* user_data);
*/
import "C"
import (
"runtime/cgo"
"unsafe"
)

Expand All @@ -38,8 +37,7 @@ type LoggingConnection struct {

//export go_log_callback_bridge
func go_log_callback_bridge(user_data unsafe.Pointer, message *C.char, message_len C.size_t) {
handle := cgo.Handle(user_data)
callback := handle.Value().(LogCallback)
callback := cgoHandleFromPointer(user_data).Value().(LogCallback)
goMessage := C.GoStringN(message, C.int(message_len))
callback(goMessage)
}
Expand All @@ -54,11 +52,11 @@ func go_log_callback_bridge(user_data unsafe.Pointer, message *C.char, message_l
//
// Returns an error if the logging connection cannot be created.
func NewLoggingConnection(callback LogCallback) (*LoggingConnection, error) {
callbackHandle := cgo.NewHandle(callback)
userData := newCgoHandlePointer(callback)
ptr := C.btck_logging_connection_create((C.btck_LogCallback)(C.go_log_callback_bridge),
unsafe.Pointer(callbackHandle), C.btck_DestroyCallback(C.go_delete_handle))
userData, C.btck_DestroyCallback(C.go_delete_handle))
if ptr == nil {
callbackHandle.Delete()
deleteCgoHandlePointer(userData)
return nil, &InternalError{"Failed to create logging connection"}
}
h := newUniqueHandle(unsafe.Pointer(ptr), loggingConnectionCFuncs{})
Expand Down
22 changes: 7 additions & 15 deletions kernel/notification_callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package kernel
*/
import "C"
import (
"runtime/cgo"
"unsafe"
)

Expand Down Expand Up @@ -39,8 +38,7 @@ const (

//export go_notify_block_tip_bridge
func go_notify_block_tip_bridge(user_data unsafe.Pointer, state C.btck_SynchronizationState, entry *C.btck_BlockTreeEntry, verification_progress C.double) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnBlockTip != nil {
goState := SynchronizationState(state)
Expand All @@ -51,8 +49,7 @@ func go_notify_block_tip_bridge(user_data unsafe.Pointer, state C.btck_Synchroni

//export go_notify_header_tip_bridge
func go_notify_header_tip_bridge(user_data unsafe.Pointer, state C.btck_SynchronizationState, height C.int64_t, timestamp C.int64_t, presync C.int) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnHeaderTip != nil {
goState := SynchronizationState(state)
Expand All @@ -62,8 +59,7 @@ func go_notify_header_tip_bridge(user_data unsafe.Pointer, state C.btck_Synchron

//export go_notify_progress_bridge
func go_notify_progress_bridge(user_data unsafe.Pointer, title *C.char, title_len C.size_t, progress_percent C.int, resume_possible C.int) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnProgress != nil {
goTitle := C.GoStringN(title, C.int(title_len))
Expand All @@ -73,8 +69,7 @@ func go_notify_progress_bridge(user_data unsafe.Pointer, title *C.char, title_le

//export go_notify_warning_set_bridge
func go_notify_warning_set_bridge(user_data unsafe.Pointer, warning C.btck_Warning, message *C.char, message_len C.size_t) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnWarningSet != nil {
goWarning := Warning(warning)
Expand All @@ -85,8 +80,7 @@ func go_notify_warning_set_bridge(user_data unsafe.Pointer, warning C.btck_Warni

//export go_notify_warning_unset_bridge
func go_notify_warning_unset_bridge(user_data unsafe.Pointer, warning C.btck_Warning) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnWarningUnset != nil {
goWarning := Warning(warning)
Expand All @@ -96,8 +90,7 @@ func go_notify_warning_unset_bridge(user_data unsafe.Pointer, warning C.btck_War

//export go_notify_flush_error_bridge
func go_notify_flush_error_bridge(user_data unsafe.Pointer, message *C.char, message_len C.size_t) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnFlushError != nil {
goMessage := C.GoStringN(message, C.int(message_len))
Expand All @@ -107,8 +100,7 @@ func go_notify_flush_error_bridge(user_data unsafe.Pointer, message *C.char, mes

//export go_notify_fatal_error_bridge
func go_notify_fatal_error_bridge(user_data unsafe.Pointer, message *C.char, message_len C.size_t) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*NotificationCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*NotificationCallbacks)

if callbacks.OnFatalError != nil {
goMessage := C.GoStringN(message, C.int(message_len))
Expand Down
13 changes: 4 additions & 9 deletions kernel/validation_interface_callbacks.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package kernel
*/
import "C"
import (
"runtime/cgo"
"unsafe"
)

Expand All @@ -21,35 +20,31 @@ type ValidationInterfaceCallbacks struct {

//export go_validation_interface_block_checked_bridge
func go_validation_interface_block_checked_bridge(user_data unsafe.Pointer, block *C.btck_Block, state *C.btck_BlockValidationState) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*ValidationInterfaceCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*ValidationInterfaceCallbacks)
if callbacks.OnBlockChecked != nil {
callbacks.OnBlockChecked(newBlock(block, true), newBlockValidationStateView(state))
}
}

//export go_validation_interface_pow_valid_block_bridge
func go_validation_interface_pow_valid_block_bridge(user_data unsafe.Pointer, block *C.btck_Block, entry *C.btck_BlockTreeEntry) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*ValidationInterfaceCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*ValidationInterfaceCallbacks)
if callbacks.OnPoWValidBlock != nil {
callbacks.OnPoWValidBlock(newBlock(block, true), &BlockTreeEntry{ptr: entry})
}
}

//export go_validation_interface_block_connected_bridge
func go_validation_interface_block_connected_bridge(user_data unsafe.Pointer, block *C.btck_Block, entry *C.btck_BlockTreeEntry) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*ValidationInterfaceCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*ValidationInterfaceCallbacks)
if callbacks.OnBlockConnected != nil {
callbacks.OnBlockConnected(newBlock(block, true), &BlockTreeEntry{ptr: entry})
}
}

//export go_validation_interface_block_disconnected_bridge
func go_validation_interface_block_disconnected_bridge(user_data unsafe.Pointer, block *C.btck_Block, entry *C.btck_BlockTreeEntry) {
handle := cgo.Handle(user_data)
callbacks := handle.Value().(*ValidationInterfaceCallbacks)
callbacks := cgoHandleFromPointer(user_data).Value().(*ValidationInterfaceCallbacks)
if callbacks.OnBlockDisconnected != nil {
callbacks.OnBlockDisconnected(newBlock(block, true), &BlockTreeEntry{ptr: entry})
}
Expand Down
4 changes: 2 additions & 2 deletions kernel/writer_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ type writerCallbackData struct {
//export go_writer_callback_bridge
func go_writer_callback_bridge(bytes unsafe.Pointer, size C.size_t, userdata unsafe.Pointer) C.int {
if size > 0 {
data := cgo.Handle(userdata).Value().(*writerCallbackData)
data := cgoHandleFromPointer(userdata).Value().(*writerCallbackData)
// Create a Go slice view of the C memory
cBytes := unsafe.Slice((*byte)(bytes), int(size))
data.buffer = append(data.buffer, cBytes...)
Expand All @@ -36,7 +36,7 @@ func writeToBytes(writerFunc func(C.btck_WriteBytes, unsafe.Pointer) C.int) (byt
handle := cgo.NewHandle(callbackData)
defer handle.Delete()

result := writerFunc((C.btck_WriteBytes)(C.go_writer_callback_bridge), unsafe.Pointer(handle))
result := writerFunc((C.btck_WriteBytes)(C.go_writer_callback_bridge), unsafe.Pointer(&handle))
if result != 0 {
return nil, false
}
Expand Down
Loading