Skip to content

Commit 35ff0de

Browse files
committed
Merge pull request #196 from pks-t/pointer-indirection
[WIP/RFC] Pointer indirection
2 parents 193deb7 + e8531dd commit 35ff0de

File tree

10 files changed

+196
-41
lines changed

10 files changed

+196
-41
lines changed

blob.go

+12-3
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,13 @@ type BlobCallbackData struct {
6060
}
6161

6262
//export blobChunkCb
63-
func blobChunkCb(buffer *C.char, maxLen C.size_t, payload unsafe.Pointer) int {
64-
data := (*BlobCallbackData)(payload)
63+
func blobChunkCb(buffer *C.char, maxLen C.size_t, handle unsafe.Pointer) int {
64+
payload := pointerHandles.Get(handle)
65+
data, ok := payload.(*BlobCallbackData)
66+
if !ok {
67+
panic("could not retrieve blob callback data")
68+
}
69+
6570
goBuf, err := data.Callback(int(maxLen))
6671
if err == io.EOF {
6772
return 0
@@ -83,8 +88,12 @@ func (repo *Repository) CreateBlobFromChunks(hintPath string, callback BlobChunk
8388
defer C.free(unsafe.Pointer(chintPath))
8489
}
8590
oid := C.git_oid{}
91+
8692
payload := &BlobCallbackData{Callback: callback}
87-
ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, unsafe.Pointer(payload))
93+
handle := pointerHandles.Track(payload)
94+
defer pointerHandles.Untrack(handle)
95+
96+
ecode := C._go_git_blob_create_fromchunks(&oid, repo.ptr, chintPath, handle)
8897
if payload.Error != nil {
8998
return nil, payload.Error
9099
}

diff.go

+36-11
Original file line numberDiff line numberDiff line change
@@ -265,16 +265,24 @@ func (diff *Diff) ForEach(cbFile DiffForEachFileCallback, detail DiffDetail) err
265265
data := &diffForEachData{
266266
FileCallback: cbFile,
267267
}
268-
ecode := C._go_git_diff_foreach(diff.ptr, 1, intHunks, intLines, unsafe.Pointer(data))
268+
269+
handle := pointerHandles.Track(data)
270+
defer pointerHandles.Untrack(handle)
271+
272+
ecode := C._go_git_diff_foreach(diff.ptr, 1, intHunks, intLines, handle)
269273
if ecode < 0 {
270274
return data.Error
271275
}
272276
return nil
273277
}
274278

275279
//export diffForEachFileCb
276-
func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe.Pointer) int {
277-
data := (*diffForEachData)(payload)
280+
func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, handle unsafe.Pointer) int {
281+
payload := pointerHandles.Get(handle)
282+
data, ok := payload.(*diffForEachData)
283+
if !ok {
284+
panic("could not retrieve data for handle")
285+
}
278286

279287
data.HunkCallback = nil
280288
if data.FileCallback != nil {
@@ -292,8 +300,12 @@ func diffForEachFileCb(delta *C.git_diff_delta, progress C.float, payload unsafe
292300
type DiffForEachHunkCallback func(DiffHunk) (DiffForEachLineCallback, error)
293301

294302
//export diffForEachHunkCb
295-
func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload unsafe.Pointer) int {
296-
data := (*diffForEachData)(payload)
303+
func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, handle unsafe.Pointer) int {
304+
payload := pointerHandles.Get(handle)
305+
data, ok := payload.(*diffForEachData)
306+
if !ok {
307+
panic("could not retrieve data for handle")
308+
}
297309

298310
data.LineCallback = nil
299311
if data.HunkCallback != nil {
@@ -311,9 +323,12 @@ func diffForEachHunkCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, payload u
311323
type DiffForEachLineCallback func(DiffLine) error
312324

313325
//export diffForEachLineCb
314-
func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, payload unsafe.Pointer) int {
315-
316-
data := (*diffForEachData)(payload)
326+
func diffForEachLineCb(delta *C.git_diff_delta, hunk *C.git_diff_hunk, line *C.git_diff_line, handle unsafe.Pointer) int {
327+
payload := pointerHandles.Get(handle)
328+
data, ok := payload.(*diffForEachData)
329+
if !ok {
330+
panic("could not retrieve data for handle")
331+
}
317332

318333
err := data.LineCallback(diffLineFromC(delta, hunk, line))
319334
if err != nil {
@@ -479,9 +494,15 @@ type diffNotifyData struct {
479494
}
480495

481496
//export diffNotifyCb
482-
func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, payload unsafe.Pointer) int {
497+
func diffNotifyCb(_diff_so_far unsafe.Pointer, delta_to_add *C.git_diff_delta, matched_pathspec *C.char, handle unsafe.Pointer) int {
483498
diff_so_far := (*C.git_diff)(_diff_so_far)
484-
data := (*diffNotifyData)(payload)
499+
500+
payload := pointerHandles.Get(handle)
501+
data, ok := payload.(*diffNotifyData)
502+
if !ok {
503+
panic("could not retrieve data for handle")
504+
}
505+
485506
if data != nil {
486507
if data.Diff == nil {
487508
data.Diff = newDiffFromC(diff_so_far)
@@ -507,6 +528,7 @@ func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *d
507528
notifyData = &diffNotifyData{
508529
Callback: opts.NotifyCallback,
509530
}
531+
510532
if opts.Pathspec != nil {
511533
cpathspec.count = C.size_t(len(opts.Pathspec))
512534
cpathspec.strings = makeCStringsFromStrings(opts.Pathspec)
@@ -527,7 +549,7 @@ func diffOptionsToC(opts *DiffOptions) (copts *C.git_diff_options, notifyData *d
527549

528550
if opts.NotifyCallback != nil {
529551
C._go_git_setup_diff_notify_callbacks(copts)
530-
copts.notify_payload = unsafe.Pointer(notifyData)
552+
copts.notify_payload = pointerHandles.Track(notifyData)
531553
}
532554
}
533555
return
@@ -539,6 +561,9 @@ func freeDiffOptions(copts *C.git_diff_options) {
539561
freeStrarray(&cpathspec)
540562
C.free(unsafe.Pointer(copts.old_prefix))
541563
C.free(unsafe.Pointer(copts.new_prefix))
564+
if copts.notify_payload != nil {
565+
pointerHandles.Untrack(copts.notify_payload)
566+
}
542567
}
543568
}
544569

git.go

+4
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,11 @@ var (
9393
ErrInvalid = errors.New("Invalid state for operation")
9494
)
9595

96+
var pointerHandles *HandleList
97+
9698
func init() {
99+
pointerHandles = NewHandleList()
100+
97101
C.git_libgit2_init()
98102

99103
// This is not something we should be doing, as we may be

handles.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package git
2+
3+
import (
4+
"fmt"
5+
"sync"
6+
"unsafe"
7+
)
8+
9+
type HandleList struct {
10+
sync.RWMutex
11+
// stores the Go pointers
12+
handles []interface{}
13+
// indicates which indices are in use
14+
set map[int]bool
15+
}
16+
17+
func NewHandleList() *HandleList {
18+
return &HandleList{
19+
handles: make([]interface{}, 5),
20+
set: make(map[int]bool),
21+
}
22+
}
23+
24+
// findUnusedSlot finds the smallest-index empty space in our
25+
// list. You must only run this function while holding a write lock.
26+
func (v *HandleList) findUnusedSlot() int {
27+
for i := 1; i < len(v.handles); i++ {
28+
isUsed := v.set[i]
29+
if !isUsed {
30+
return i
31+
}
32+
}
33+
34+
// reaching here means we've run out of entries so append and
35+
// return the new index, which is equal to the old length.
36+
slot := len(v.handles)
37+
v.handles = append(v.handles, nil)
38+
39+
return slot
40+
}
41+
42+
// Track adds the given pointer to the list of pointers to track and
43+
// returns a pointer value which can be passed to C as an opaque
44+
// pointer.
45+
func (v *HandleList) Track(pointer interface{}) unsafe.Pointer {
46+
v.Lock()
47+
48+
slot := v.findUnusedSlot()
49+
v.handles[slot] = pointer
50+
v.set[slot] = true
51+
52+
v.Unlock()
53+
54+
return unsafe.Pointer(&slot)
55+
}
56+
57+
// Untrack stops tracking the pointer given by the handle
58+
func (v *HandleList) Untrack(handle unsafe.Pointer) {
59+
slot := *(*int)(handle)
60+
61+
v.Lock()
62+
63+
v.handles[slot] = nil
64+
delete(v.set, slot)
65+
66+
v.Unlock()
67+
}
68+
69+
// Get retrieves the pointer from the given handle
70+
func (v *HandleList) Get(handle unsafe.Pointer) interface{} {
71+
slot := *(*int)(handle)
72+
73+
v.RLock()
74+
75+
if _, ok := v.set[slot]; !ok {
76+
panic(fmt.Sprintf("invalid pointer handle: %p", handle))
77+
}
78+
79+
ptr := v.handles[slot]
80+
81+
v.RUnlock()
82+
83+
return ptr
84+
}

index.go

+17-11
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,17 @@ func (v *Index) AddAll(pathspecs []string, flags IndexAddOpts, callback IndexMat
162162
runtime.LockOSThread()
163163
defer runtime.UnlockOSThread()
164164

165-
var cb *IndexMatchedPathCallback
165+
var handle unsafe.Pointer
166166
if callback != nil {
167-
cb = &callback
167+
handle = pointerHandles.Track(callback)
168+
defer pointerHandles.Untrack(handle)
168169
}
169170

170171
ret := C._go_git_index_add_all(
171172
v.ptr,
172173
&cpathspecs,
173174
C.uint(flags),
174-
unsafe.Pointer(cb),
175+
handle,
175176
)
176177
if ret < 0 {
177178
return MakeGitError(ret)
@@ -188,15 +189,16 @@ func (v *Index) UpdateAll(pathspecs []string, callback IndexMatchedPathCallback)
188189
runtime.LockOSThread()
189190
defer runtime.UnlockOSThread()
190191

191-
var cb *IndexMatchedPathCallback
192+
var handle unsafe.Pointer
192193
if callback != nil {
193-
cb = &callback
194+
handle = pointerHandles.Track(callback)
195+
defer pointerHandles.Untrack(handle)
194196
}
195197

196198
ret := C._go_git_index_update_all(
197199
v.ptr,
198200
&cpathspecs,
199-
unsafe.Pointer(cb),
201+
handle,
200202
)
201203
if ret < 0 {
202204
return MakeGitError(ret)
@@ -213,15 +215,16 @@ func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback)
213215
runtime.LockOSThread()
214216
defer runtime.UnlockOSThread()
215217

216-
var cb *IndexMatchedPathCallback
218+
var handle unsafe.Pointer
217219
if callback != nil {
218-
cb = &callback
220+
handle = pointerHandles.Track(callback)
221+
defer pointerHandles.Untrack(handle)
219222
}
220223

221224
ret := C._go_git_index_remove_all(
222225
v.ptr,
223226
&cpathspecs,
224-
unsafe.Pointer(cb),
227+
handle,
225228
)
226229
if ret < 0 {
227230
return MakeGitError(ret)
@@ -231,8 +234,11 @@ func (v *Index) RemoveAll(pathspecs []string, callback IndexMatchedPathCallback)
231234

232235
//export indexMatchedPathCallback
233236
func indexMatchedPathCallback(cPath, cMatchedPathspec *C.char, payload unsafe.Pointer) int {
234-
callback := (*IndexMatchedPathCallback)(payload)
235-
return (*callback)(C.GoString(cPath), C.GoString(cMatchedPathspec))
237+
if callback, ok := pointerHandles.Get(payload).(IndexMatchedPathCallback); ok {
238+
return callback(C.GoString(cPath), C.GoString(cMatchedPathspec))
239+
} else {
240+
panic("invalid matched path callback")
241+
}
236242
}
237243

238244
func (v *Index) RemoveByPath(path string) error {

odb.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,12 @@ type foreachData struct {
9898
}
9999

100100
//export odbForEachCb
101-
func odbForEachCb(id *C.git_oid, payload unsafe.Pointer) int {
102-
data := (*foreachData)(payload)
101+
func odbForEachCb(id *C.git_oid, handle unsafe.Pointer) int {
102+
data, ok := pointerHandles.Get(handle).(*foreachData)
103+
104+
if !ok {
105+
panic("could not retrieve handle")
106+
}
103107

104108
err := data.callback(newOidFromC(id))
105109
if err != nil {
@@ -119,7 +123,10 @@ func (v *Odb) ForEach(callback OdbForEachCallback) error {
119123
runtime.LockOSThread()
120124
defer runtime.UnlockOSThread()
121125

122-
ret := C._go_git_odb_foreach(v.ptr, unsafe.Pointer(&data))
126+
handle := pointerHandles.Track(&data)
127+
defer pointerHandles.Untrack(handle)
128+
129+
ret := C._go_git_odb_foreach(v.ptr, handle)
123130
if ret == C.GIT_EUSER {
124131
return data.err
125132
} else if ret < 0 {

odb_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func TestOdbForeach(t *testing.T) {
8181

8282
checkFatal(t, err)
8383
if count != expect {
84-
t.Fatalf("Expected %v objects, got %v")
84+
t.Fatalf("Expected %v objects, got %v", expect, count)
8585
}
8686

8787
expect = 1

packbuilder.go

+10-3
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,13 @@ type packbuilderCbData struct {
110110
}
111111

112112
//export packbuilderForEachCb
113-
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, payload unsafe.Pointer) int {
114-
data := (*packbuilderCbData)(payload)
113+
func packbuilderForEachCb(buf unsafe.Pointer, size C.size_t, handle unsafe.Pointer) int {
114+
payload := pointerHandles.Get(handle)
115+
data, ok := payload.(*packbuilderCbData)
116+
if !ok {
117+
panic("could not get packbuilder CB data")
118+
}
119+
115120
slice := C.GoBytes(buf, C.int(size))
116121

117122
err := data.callback(slice)
@@ -130,11 +135,13 @@ func (pb *Packbuilder) ForEach(callback PackbuilderForeachCallback) error {
130135
callback: callback,
131136
err: nil,
132137
}
138+
handle := pointerHandles.Track(&data)
139+
defer pointerHandles.Untrack(handle)
133140

134141
runtime.LockOSThread()
135142
defer runtime.UnlockOSThread()
136143

137-
err := C._go_git_packbuilder_foreach(pb.ptr, unsafe.Pointer(&data))
144+
err := C._go_git_packbuilder_foreach(pb.ptr, handle)
138145
if err == C.GIT_EUSER {
139146
return data.err
140147
}

0 commit comments

Comments
 (0)