handles: use real pointers to keep track of handles
With the change to 1.6 rules, we couldn't use the Go pointers, so we went with casting the list indices into pointers. The runtime does not like this, however. It will sometimes detect that we have a pointer with a very small value and consider it an invalid pointer, bringing down the application with it. Work around that by asking libc for the smallest amount of memory it'll give us so we have an actual allocated pointer to use. We then use this pointer value as the key in our map to find the Go object we're tracking.
This commit is contained in:
parent
f05417aaba
commit
f1240e6565
56
handles.go
56
handles.go
|
@ -1,5 +1,9 @@
|
|||
package git
|
||||
|
||||
/*
|
||||
#include <stdlib.h>
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
@ -9,75 +13,45 @@ import (
|
|||
type HandleList struct {
|
||||
sync.RWMutex
|
||||
// stores the Go pointers
|
||||
handles []interface{}
|
||||
// Indicates which indices are in use.
|
||||
set map[int]bool
|
||||
handles map[unsafe.Pointer]interface{}
|
||||
}
|
||||
|
||||
func NewHandleList() *HandleList {
|
||||
return &HandleList{
|
||||
handles: make([]interface{}, 5),
|
||||
set: make(map[int]bool),
|
||||
handles: make(map[unsafe.Pointer]interface{}),
|
||||
}
|
||||
}
|
||||
|
||||
// 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() int {
|
||||
for i := 1; i < len(v.handles); i++ {
|
||||
if !v.set[i] {
|
||||
return 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 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 {
|
||||
handle := C.malloc(1)
|
||||
|
||||
v.Lock()
|
||||
|
||||
slot := v.findUnusedSlot()
|
||||
v.handles[slot] = pointer
|
||||
v.set[slot] = true
|
||||
|
||||
v.handles[handle] = pointer
|
||||
v.Unlock()
|
||||
|
||||
return unsafe.Pointer(uintptr(slot))
|
||||
return handle
|
||||
}
|
||||
|
||||
// Untrack stops tracking the pointer given by the handle
|
||||
func (v *HandleList) Untrack(handle unsafe.Pointer) {
|
||||
slot := int(uintptr(handle))
|
||||
|
||||
v.Lock()
|
||||
|
||||
v.handles[slot] = nil
|
||||
delete(v.set, slot)
|
||||
|
||||
delete(v.handles, handle)
|
||||
C.free(handle)
|
||||
v.Unlock()
|
||||
}
|
||||
|
||||
// Get retrieves the pointer from the given handle
|
||||
func (v *HandleList) Get(handle unsafe.Pointer) interface{} {
|
||||
slot := int(uintptr(handle))
|
||||
|
||||
v.RLock()
|
||||
defer v.RUnlock()
|
||||
|
||||
if !v.set[slot] {
|
||||
ptr, ok := v.handles[handle]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("invalid pointer handle: %p", handle))
|
||||
}
|
||||
|
||||
ptr := v.handles[slot]
|
||||
|
||||
v.RUnlock()
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue