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.
This commit is contained in:
Carlos Martín Nieto 2015-04-21 13:14:22 +02:00 committed by Patrick Steinhardt
parent 193deb7ae3
commit 7750e85fd1
2 changed files with 86 additions and 0 deletions

4
git.go
View File

@ -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

82
handles.go Normal file
View File

@ -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
}