Skip to content

Commit 0e3d9ae

Browse files
committed
cgo: iterate implementation
1 parent f81787b commit 0e3d9ae

File tree

2 files changed

+63
-31
lines changed

2 files changed

+63
-31
lines changed

internal/cgo/handle.go

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ import (
1414
"sync"
1515
)
1616

17-
// Handle provides a safe representation to communicate Go values between
18-
// C and Go. The zero value of a handle is not a valid handle, and thus
19-
// safe to use as a sentinel in C APIs.
17+
// Handle provides a safe representation to pass Go values between C and
18+
// Go back and forth. The zero value of a handle is not a valid handle,
19+
// and thus safe to use as a sentinel in C APIs.
2020
//
2121
// The underlying type of Handle may change, but the value is guaranteed
2222
// to fit in an integer type that is large enough to hold the bit pattern
@@ -25,14 +25,14 @@ import (
2525
// package main
2626
//
2727
// /*
28-
// extern void GoPrint(unsigned long long handle);
29-
// void printFromGo(unsigned long long handle);
28+
// extern void MyGoPrint(unsigned long long handle);
29+
// void myprint(unsigned long long handle);
3030
// */
3131
// import "C"
3232
// import "runtime/cgo"
3333
//
34-
// //export GoPrint
35-
// func GoPrint(handle C.ulonglong) {
34+
// //export MyGoPrint
35+
// func MyGoPrint(handle C.ulonglong) {
3636
// h := cgo.Handle(handle)
3737
// val := h.Value().(int)
3838
// println(val)
@@ -41,18 +41,18 @@ import (
4141
//
4242
// func main() {
4343
// val := 42
44-
//
45-
// C.printFromGo(C.ulonglong(cgo.NewHandle(val))) // prints 42
44+
// C.myprint(C.ulonglong(cgo.NewHandle(val)))
45+
// // Output: 42
4646
// }
4747
//
4848
// and on the C side:
4949
//
50-
// // This function is from Go side.
51-
// extern void GoPrint(unsigned long long handle);
50+
// // A Go function
51+
// extern void MyGoPrint(unsigned long long handle);
5252
//
5353
// // A C function
54-
// void printFromGo(unsigned long long handle) {
55-
// GoPrint(handle);
54+
// void myprint(unsigned long long handle) {
55+
// MyGoPrint(handle);
5656
// }
5757
type Handle uintptr
5858

@@ -77,23 +77,29 @@ func NewHandle(v interface{}) Handle {
7777
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
7878
reflect.Map, reflect.Chan, reflect.Func:
7979
if rv.IsNil() {
80-
panic("cannot use handle for nil value")
80+
panic("cgo: cannot use Handle for nil value")
8181
}
8282

8383
k = rv.Pointer()
8484
default:
85-
k = reflect.ValueOf(&v).Pointer()
85+
// Wrap and turn a value parameter into a pointer. This enables
86+
// us to always store the passing object as a pointer, and helps
87+
// to identify which of whose are initially pointers or values
88+
// when Value is called.
89+
v = &wrap{v}
90+
k = reflect.ValueOf(v).Pointer()
8691
}
8792

88-
// v escapes to the heap, always. As Go do not have a moving GC (and
89-
// possibly lasts true for a long future), it is safe to use its
90-
// pointer address as the key of the global map at this moment.
91-
// The implementation must be reconsidered if moving GC is
92-
// introduced internally.
93+
// v was escaped to the heap because of reflection. As Go do not have
94+
// a moving GC (and possibly lasts true for a long future), it is
95+
// safe to use its pointer address as the key of the global map at
96+
// this moment. The implementation must be reconsidered if moving GC
97+
// is introduced internally in the runtime.
9398
actual, loaded := m.LoadOrStore(k, v)
9499
if !loaded {
95100
return Handle(k)
96101
}
102+
97103
arv := reflect.ValueOf(actual)
98104
switch arv.Kind() {
99105
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice,
@@ -104,16 +110,15 @@ func NewHandle(v interface{}) Handle {
104110
return Handle(k)
105111
}
106112

107-
// If the loaded actual value is inconsistent with the new
108-
// value, it means the address has been used for different
109-
// objects, and we should fallthrough, see comments below.
110-
fallthrough
113+
// If the loaded pointer is inconsistent with the new pointer,
114+
// it means the address has been used for different objects
115+
// because of GC and its address is reused for a new Go object,
116+
// meaning that the Handle does not call Delete explicitly when
117+
// the old Go value is not needed. Consider this as a misuse of
118+
// a handle, do panic.
119+
panic("cgo: misuse of a Handle")
111120
default:
112-
// If a Go value is garbage collected and its address is reused
113-
// for a new Go value, meaning that the Handle does not call
114-
// Delete explicitly when the old Go value is not needed.
115-
// Consider this as a misuse of a handle, do panic.
116-
panic("misuse of a handle")
121+
panic("cgo: Handle implementation has an internal bug")
117122
}
118123
}
119124

@@ -125,7 +130,7 @@ func NewHandle(v interface{}) Handle {
125130
func (h Handle) Delete() {
126131
_, ok := m.LoadAndDelete(uintptr(h))
127132
if !ok {
128-
panic("misuse of a handle")
133+
panic("cgo: misuse of an invalid Handle")
129134
}
130135
}
131136

@@ -135,9 +140,17 @@ func (h Handle) Delete() {
135140
func (h Handle) Value() interface{} {
136141
v, ok := m.Load(uintptr(h))
137142
if !ok {
138-
panic("misuse of a handle")
143+
panic("cgo: misuse of an invalid Handle")
144+
}
145+
if wv, ok := v.(*wrap); ok {
146+
return wv.v
139147
}
140148
return v
141149
}
142150

143151
var m = &sync.Map{} // map[uintptr]interface{}
152+
153+
// wrap wraps a Go value.
154+
type wrap struct {
155+
v interface{}
156+
}

internal/cgo/handle_test.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,22 @@ func TestFuncHandle(t *testing.T) {
122122
t.Fatalf("different methods should have different handles")
123123
}
124124
}
125+
func BenchmarkHandle(b *testing.B) {
126+
b.Run("non-concurrent", func(b *testing.B) {
127+
for i := 0; i < b.N; i++ {
128+
h := NewHandle(i)
129+
_ = h.Value()
130+
h.Delete()
131+
}
132+
})
133+
b.Run("concurrent", func(b *testing.B) {
134+
b.RunParallel(func(pb *testing.PB) {
135+
var v int
136+
for pb.Next() {
137+
h := NewHandle(v)
138+
_ = h.Value()
139+
h.Delete()
140+
}
141+
})
142+
})
143+
}

0 commit comments

Comments
 (0)