From 7750e85fd1ff1006a28a5e292c9bc7ce3e12b586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Tue, 21 Apr 2015 13:14:22 +0200 Subject: [PATCH] Introduce an indirection layer for pointers As the Go runtime can move stacks at any point and the C code runs concurrently with the rest of the system, we cannot assume that the payloads we give to the C code will stay valid for any particular duration. We must therefore give the C code handles which we can then look up in our own list when the callbacks get called. --- git.go | 4 +++ handles.go | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 handles.go diff --git a/git.go b/git.go index 9496d2d..4f1a65e 100644 --- a/git.go +++ b/git.go @@ -93,7 +93,11 @@ var ( ErrInvalid = errors.New("Invalid state for operation") ) +var pointerHandles *HandleList + func init() { + pointerHandles = NewHandleList() + C.git_libgit2_init() // This is not something we should be doing, as we may be diff --git a/handles.go b/handles.go new file mode 100644 index 0000000..c0d1889 --- /dev/null +++ b/handles.go @@ -0,0 +1,82 @@ +package git + +import ( + "sync" + "unsafe" +) + +type HandleList struct { + sync.RWMutex + // stores the Go pointers + handles []interface{} + // indicates which indices are in use + set map[uintptr]bool +} + +func NewHandleList() *HandleList { + return &HandleList{ + handles: make([]interface{}, 5), + } +} + +// findUnusedSlot finds the smallest-index empty space in our +// list. You must only run this function while holding a write lock. +func (v *HandleList) findUnusedSlot() uintptr { + for i := 0; i < len(v.handles); i++ { + isUsed := v.set[uintptr(i)] + if !isUsed { + return uintptr(i) + } + } + + // reaching here means we've run out of entries so append and + // return the new index, which is equal to the old length. + slot := len(v.handles) + v.handles = append(v.handles, nil) + + return uintptr(slot) +} + +// Track adds the given pointer to the list of pointers to track and +// returns a pointer value which can be passed to C as an opaque +// pointer. +func (v *HandleList) Track(pointer interface{}) unsafe.Pointer { + v.Lock() + + slot := v.findUnusedSlot() + v.handles[slot] = pointer + v.set[slot] = true + + v.Unlock() + + return unsafe.Pointer(slot) +} + +// Untrack stops tracking the pointer given by the handle +func (v *HandleList) Untrack(handle unsafe.Pointer) { + slot := uintptr(handle) + + v.Lock() + + v.handles[slot] = nil + delete(v.set, slot) + + v.Unlock() +} + +// Get retrieves the pointer from the given handle +func (v *HandleList) Get(handle unsafe.Pointer) interface{} { + slot := uintptr(handle) + + v.RLock() + + if _, ok := v.set[slot]; !ok { + panic("invalid pointer handle") + } + + ptr := v.handles[slot] + + v.RUnlock() + + return ptr +}