From e8da5affe7ada724b71c6603c81d534752d4be6b Mon Sep 17 00:00:00 2001 From: Carlos Martín Nieto Date: Wed, 14 Jan 2015 13:52:52 +0100 Subject: Link dynamically to libgit2 With libgit2 v0.22 released, we can expect its API and ABI to remain stable when installed on the system. Linking dynamically allows us to use the go tool alone to build and install the package. --- git.go | 1 + 1 file changed, 1 insertion(+) (limited to 'git.go') diff --git a/git.go b/git.go index 8e78710..9496d2d 100644 --- a/git.go +++ b/git.go @@ -3,6 +3,7 @@ package git /* #include #include +#cgo pkg-config: libgit2 */ import "C" import ( -- cgit v1.2.3 From 7750e85fd1ff1006a28a5e292c9bc7ce3e12b586 Mon Sep 17 00:00:00 2001 From: Carlos Martín Nieto Date: Tue, 21 Apr 2015 13:14:22 +0200 Subject: 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 (limited to 'git.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 +} -- cgit v1.2.3