-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
1,192 additions
and
0 deletions.
There are no files selected for viewing
25 changes: 25 additions & 0 deletions
25
content/assets/cgo-handle/cgo1/bench-2021-06-10-19:57:43.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
goos: darwin | ||
goarch: arm64 | ||
pkg: cgo-handle/cgo1 | ||
BenchmarkHandle/non-concurrent-8 2822158 405.0 ns/op | ||
BenchmarkHandle/non-concurrent-8 2935992 409.3 ns/op | ||
BenchmarkHandle/non-concurrent-8 2932722 417.4 ns/op | ||
BenchmarkHandle/non-concurrent-8 2917302 409.5 ns/op | ||
BenchmarkHandle/non-concurrent-8 2921462 409.1 ns/op | ||
BenchmarkHandle/non-concurrent-8 2953590 407.4 ns/op | ||
BenchmarkHandle/non-concurrent-8 2948713 407.0 ns/op | ||
BenchmarkHandle/non-concurrent-8 2955361 405.4 ns/op | ||
BenchmarkHandle/non-concurrent-8 2833503 419.7 ns/op | ||
BenchmarkHandle/non-concurrent-8 2925784 405.4 ns/op | ||
BenchmarkHandle/concurrent-8 1562476 767.6 ns/op | ||
BenchmarkHandle/concurrent-8 1561358 767.9 ns/op | ||
BenchmarkHandle/concurrent-8 1560795 769.4 ns/op | ||
BenchmarkHandle/concurrent-8 1563916 768.8 ns/op | ||
BenchmarkHandle/concurrent-8 1518699 768.2 ns/op | ||
BenchmarkHandle/concurrent-8 1559850 769.1 ns/op | ||
BenchmarkHandle/concurrent-8 1557451 768.2 ns/op | ||
BenchmarkHandle/concurrent-8 1560009 777.5 ns/op | ||
BenchmarkHandle/concurrent-8 1510149 766.8 ns/op | ||
BenchmarkHandle/concurrent-8 1500530 767.7 ns/op | ||
PASS | ||
ok cgo-handle/cgo1 36.071s |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
// Copyright 2021 The golang.design Initiative Authors. | ||
// All rights reserved. Use of this source code is governed | ||
// by a MIT license that can be found in the LICENSE file. | ||
// | ||
// Written by Changkun Ou <changkun.de> | ||
|
||
// Package cgo is an implementation of golang.org/issue/37033. | ||
// | ||
// See golang.org/cl/294670 for code review discussion. | ||
package cgo1 | ||
|
||
import ( | ||
"reflect" | ||
"sync" | ||
) | ||
|
||
// Handle provides a way to pass values that contain Go pointers | ||
// (pointers to memory allocated by Go) between Go and C without | ||
// breaking the cgo pointer passing rules. A Handle is an integer | ||
// value that can represent any Go value. A Handle can be passed | ||
// through C and back to Go, and Go code can use the Handle to | ||
// retrieve the original Go value. | ||
// | ||
// The underlying type of Handle is guaranteed to fit in an integer type | ||
// that is large enough to hold the bit pattern of any pointer. The zero | ||
// value of a Handle is not valid, and thus is safe to use as a sentinel | ||
// in C APIs. | ||
// | ||
// For instance, on the Go side: | ||
// | ||
// package main | ||
// | ||
// /* | ||
// #include <stdint.h> // for uintptr_t | ||
// | ||
// extern void MyGoPrint(uintptr_t handle); | ||
// void myprint(uintptr_t handle); | ||
// */ | ||
// import "C" | ||
// import "runtime/cgo" | ||
// | ||
// //export MyGoPrint | ||
// func MyGoPrint(handle C.uintptr_t) { | ||
// h := cgo.Handle(handle) | ||
// val := h.Value().(string) | ||
// println(val) | ||
// h.Delete() | ||
// } | ||
// | ||
// func main() { | ||
// val := "hello Go" | ||
// C.myprint(C.uintptr_t(cgo.NewHandle(val))) | ||
// // Output: hello Go | ||
// } | ||
// | ||
// and on the C side: | ||
// | ||
// #include <stdint.h> // for uintptr_t | ||
// | ||
// // A Go function | ||
// extern void MyGoPrint(uintptr_t handle); | ||
// | ||
// // A C function | ||
// void myprint(uintptr_t handle) { | ||
// MyGoPrint(handle); | ||
// } | ||
type Handle uintptr | ||
|
||
// NewHandle returns a handle for a given value. | ||
// | ||
// The handle is valid until the program calls Delete on it. The handle | ||
// uses resources, and this package assumes that C code may hold on to | ||
// the handle, so a program must explicitly call Delete when the handle | ||
// is no longer needed. | ||
// | ||
// The intended use is to pass the returned handle to C code, which | ||
// passes it back to Go, which calls Value. | ||
func NewHandle(v interface{}) Handle { | ||
var k uintptr | ||
|
||
rv := reflect.ValueOf(v) | ||
switch rv.Kind() { | ||
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice, | ||
reflect.Map, reflect.Chan, reflect.Func: | ||
if rv.IsNil() { | ||
panic("cgo: cannot use Handle for nil value") | ||
} | ||
|
||
k = rv.Pointer() | ||
default: | ||
// Wrap and turn a value parameter into a pointer. This enables | ||
// us to always store the passing object as a pointer, and helps | ||
// to identify which of whose are initially pointers or values | ||
// when Value is called. | ||
v = &wrap{v} | ||
k = reflect.ValueOf(v).Pointer() | ||
} | ||
|
||
// v was escaped to the heap because of reflection. As Go do not have | ||
// a moving GC (and possibly lasts true for a long future), it is | ||
// safe to use its pointer address as the key of the global map at | ||
// this moment. The implementation must be reconsidered if moving GC | ||
// is introduced internally in the runtime. | ||
actual, loaded := m.LoadOrStore(k, v) | ||
if !loaded { | ||
return Handle(k) | ||
} | ||
|
||
arv := reflect.ValueOf(actual) | ||
switch arv.Kind() { | ||
case reflect.Ptr, reflect.UnsafePointer, reflect.Slice, | ||
reflect.Map, reflect.Chan, reflect.Func: | ||
// The underlying object of the given Go value already have | ||
// its existing handle. | ||
if arv.Pointer() == k { | ||
return Handle(k) | ||
} | ||
|
||
// If the loaded pointer is inconsistent with the new pointer, | ||
// it means the address has been used for different objects | ||
// because of GC and its address is reused for a new Go object, | ||
// meaning that the Handle does not call Delete explicitly when | ||
// the old Go value is not needed. Consider this as a misuse of | ||
// a handle, do panic. | ||
panic("cgo: misuse of a Handle") | ||
default: | ||
panic("cgo: Handle implementation has an internal bug") | ||
} | ||
} | ||
|
||
// Value returns the associated Go value for a valid handle. | ||
// | ||
// The method panics if the handle is invalid. | ||
func (h Handle) Value() interface{} { | ||
v, ok := m.Load(uintptr(h)) | ||
if !ok { | ||
panic("cgo: misuse of an invalid Handle") | ||
} | ||
if wv, ok := v.(*wrap); ok { | ||
return wv.v | ||
} | ||
return v | ||
} | ||
|
||
// Delete invalidates a handle. This method should only be called once | ||
// the program no longer needs to pass the handle to C and the C code | ||
// no longer has a copy of the handle value. | ||
// | ||
// The method panics if the handle is invalid. | ||
func (h Handle) Delete() { | ||
_, ok := m.LoadAndDelete(uintptr(h)) | ||
if !ok { | ||
panic("cgo: misuse of an invalid Handle") | ||
} | ||
} | ||
|
||
var m = &sync.Map{} // map[uintptr]interface{} | ||
|
||
// wrap wraps a Go value. | ||
type wrap struct{ v interface{} } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Copyright 2021 The golang.design Initiative Authors. | ||
// All rights reserved. Use of this source code is governed | ||
// by a MIT license that can be found in the LICENSE file. | ||
// | ||
// Written by Changkun Ou <changkun.de> | ||
|
||
package cgo1 | ||
|
||
import ( | ||
"testing" | ||
) | ||
|
||
func TestValueHandle(t *testing.T) { | ||
v := 42 | ||
|
||
h1 := NewHandle(v) | ||
h2 := NewHandle(v) | ||
|
||
if uintptr(h1) == uintptr(h2) { | ||
t.Fatalf("duplicated Go values should have different handles") | ||
} | ||
|
||
h1v := h1.Value().(int) | ||
h2v := h2.Value().(int) | ||
if h1v != h2v { | ||
t.Fatalf("the Value of duplicated Go values are different: want %d, got %d", h1v, h2v) | ||
} | ||
if h1v != v { | ||
t.Fatalf("the Value of a handle does not match origin: want %v, got %v", v, h1v) | ||
} | ||
|
||
h1.Delete() | ||
h2.Delete() | ||
|
||
siz := 0 | ||
m.Range(func(k, v interface{}) bool { | ||
siz++ | ||
return true | ||
}) | ||
if siz != 0 { | ||
t.Fatalf("handles are not deleted, want: %d, got %d", 0, siz) | ||
} | ||
} | ||
|
||
func TestPointerHandle(t *testing.T) { | ||
v := 42 | ||
|
||
p1 := &v | ||
p2 := &v | ||
|
||
h1 := NewHandle(p1) | ||
h2 := NewHandle(p2) | ||
|
||
if uintptr(h1) != uintptr(h2) { | ||
t.Fatalf("pointers to the same value should have same handle") | ||
} | ||
|
||
h1v := h1.Value().(*int) | ||
h2v := h2.Value().(*int) | ||
if h1v != h2v { | ||
t.Fatalf("the Value of a handle does not match origin: want %v, got %v", v, h1v) | ||
} | ||
|
||
h1.Delete() | ||
|
||
siz := 0 | ||
m.Range(func(k, v interface{}) bool { | ||
siz++ | ||
return true | ||
}) | ||
if siz != 0 { | ||
t.Fatalf("handles are not deleted: want %d, got %d", 0, siz) | ||
} | ||
|
||
defer func() { | ||
if r := recover(); r != nil { | ||
return | ||
} | ||
t.Fatalf("double Delete on a same handle did not trigger a panic") | ||
}() | ||
|
||
h2.Delete() | ||
} | ||
|
||
func TestNilHandle(t *testing.T) { | ||
var v *int | ||
|
||
defer func() { | ||
if r := recover(); r != nil { | ||
return | ||
} | ||
t.Fatalf("nil should not be created as a handle successfully") | ||
}() | ||
|
||
_ = NewHandle(v) | ||
} | ||
|
||
func f1() {} | ||
func f2() {} | ||
|
||
type foo struct{} | ||
|
||
func (f *foo) bar() {} | ||
func (f *foo) wow() {} | ||
|
||
func TestFuncHandle(t *testing.T) { | ||
h1 := NewHandle(f1) | ||
h2 := NewHandle(f2) | ||
h3 := NewHandle(f2) | ||
|
||
if h1 == h2 { | ||
t.Fatalf("different functions should have different handles") | ||
} | ||
if h2 != h3 { | ||
t.Fatalf("same functions should have same handles") | ||
} | ||
|
||
f := foo{} | ||
h4 := NewHandle(f.bar) | ||
h5 := NewHandle(f.bar) | ||
h6 := NewHandle(f.wow) | ||
|
||
if h4 != h5 { | ||
t.Fatalf("same methods should have same handles") | ||
} | ||
|
||
if h5 == h6 { | ||
t.Fatalf("different methods should have different handles") | ||
} | ||
} | ||
func BenchmarkHandle(b *testing.B) { | ||
b.Run("non-concurrent", func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
h := NewHandle(i) | ||
_ = h.Value() | ||
h.Delete() | ||
} | ||
}) | ||
b.Run("concurrent", func(b *testing.B) { | ||
b.RunParallel(func(pb *testing.PB) { | ||
var v int | ||
for pb.Next() { | ||
h := NewHandle(v) | ||
_ = h.Value() | ||
h.Delete() | ||
} | ||
}) | ||
}) | ||
} |
25 changes: 25 additions & 0 deletions
25
content/assets/cgo-handle/cgo2/bench-2021-06-10-19:56:57.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
goos: darwin | ||
goarch: arm64 | ||
pkg: cgo-handle/cgo2 | ||
BenchmarkHandle/non-concurrent-8 2972786 400.8 ns/op | ||
BenchmarkHandle/non-concurrent-8 3040192 390.4 ns/op | ||
BenchmarkHandle/non-concurrent-8 3058515 391.1 ns/op | ||
BenchmarkHandle/non-concurrent-8 3036595 391.0 ns/op | ||
BenchmarkHandle/non-concurrent-8 3050779 392.0 ns/op | ||
BenchmarkHandle/non-concurrent-8 3042757 389.9 ns/op | ||
BenchmarkHandle/non-concurrent-8 3020358 404.6 ns/op | ||
BenchmarkHandle/non-concurrent-8 3008872 396.0 ns/op | ||
BenchmarkHandle/non-concurrent-8 3024003 392.7 ns/op | ||
BenchmarkHandle/non-concurrent-8 3051001 392.9 ns/op | ||
BenchmarkHandle/concurrent-8 1586451 755.9 ns/op | ||
BenchmarkHandle/concurrent-8 1588989 754.7 ns/op | ||
BenchmarkHandle/concurrent-8 1543646 757.2 ns/op | ||
BenchmarkHandle/concurrent-8 1586863 755.3 ns/op | ||
BenchmarkHandle/concurrent-8 1584315 758.5 ns/op | ||
BenchmarkHandle/concurrent-8 1584865 759.3 ns/op | ||
BenchmarkHandle/concurrent-8 1575646 769.2 ns/op | ||
BenchmarkHandle/concurrent-8 1496425 760.6 ns/op | ||
BenchmarkHandle/concurrent-8 1572453 759.0 ns/op | ||
BenchmarkHandle/concurrent-8 1585522 779.9 ns/op | ||
PASS | ||
ok cgo-handle/cgo2 35.840s |
Oops, something went wrong.