@@ -17,6 +17,12 @@ limitations under the License.
1717
1818package cgo
1919
20+ import (
21+ "fmt"
22+ "sync/atomic"
23+ "unsafe"
24+ )
25+
2026// Handle is an alternative implementation of cgo.Handle introduced by
2127// Go 1.17, see https://pkg.go.dev/runtime/cgo. This implementation
2228// optimizes performance in use cases related to plugins. It is intended
@@ -43,12 +49,18 @@ package cgo
4349// The usage in other contexts is discuraged.
4450type Handle uintptr
4551
46- // MaxHandle is the largest value that an Handle can hold
47- const MaxHandle = 256 - 1
52+ const (
53+ // MaxHandle is the largest value that an Handle can hold
54+ MaxHandle = 256 - 1
55+
56+ // max number of times we're willing to iterate over the vector of reusable
57+ // handles to do compare-and-swap before giving up
58+ maxNewHandleRounds = 20
59+ )
4860
4961var (
50- handles [MaxHandle + 1 ]interface {} // [int]interface{}
51- noHandle int = 0
62+ handles [MaxHandle + 1 ]unsafe. Pointer // [int]* interface{}
63+ noHandle unsafe. Pointer = nil
5264)
5365
5466func init () {
@@ -72,24 +84,41 @@ func init() {
7284//
7385// This function is not thread-safe.
7486func NewHandle (v interface {}) Handle {
75- for i := 1 ; i <= MaxHandle ; i ++ {
76- if handles [i ] == & noHandle {
77- handles [i ] = v
78- return Handle (i )
87+ rounds := 0
88+ for h := uintptr (1 ); ; h ++ {
89+ // we acquired ownership of an handle, return it
90+ // note: we attempt accessing slots 1..MaxHandle (included)
91+ if atomic .CompareAndSwapPointer (& handles [h ], noHandle , (unsafe .Pointer )(& v )) {
92+ return Handle (h )
7993 }
94+
95+ // we haven't acquired a handle, but we can try with the next one
96+ if h < MaxHandle {
97+ continue
98+ }
99+
100+ // we iterated over the whole vector of handles, so we get back to start
101+ // and try again with another round. Once we do this too many times,
102+ // we have no choice if not panic-ing
103+ h = uintptr (0 ) // note: will be incremented when continuing
104+ if rounds < maxNewHandleRounds {
105+ rounds ++
106+ continue
107+ }
108+
109+ panic (fmt .Sprintf ("plugin-sdk-go/cgo: could not obtain a new handle after round #%d" , rounds ))
80110 }
81- panic ("plugin-sdk-go/cgo: ran out of handle space" )
82111}
83112
84113// Value returns the associated Go value for a valid handle.
85114//
86115// The method panics if the handle is invalid.
87116// This function is not thread-safe.
88117func (h Handle ) Value () interface {} {
89- if h > MaxHandle || handles [h ] == & noHandle {
90- panic ("plugin-sdk-go/cgo: misuse of an invalid Handle" )
118+ if h > MaxHandle || atomic . LoadPointer ( & handles [h ]) == noHandle {
119+ panic (fmt . Sprintf ( "plugin-sdk-go/cgo: misuse (value) of an invalid Handle %d" , h ) )
91120 }
92- return handles [h ]
121+ return * ( * interface {})( atomic . LoadPointer ( & handles [h ]))
93122}
94123
95124// Delete invalidates a handle. This method should only be called once
@@ -99,14 +128,14 @@ func (h Handle) Value() interface{} {
99128// The method panics if the handle is invalid.
100129// This function is not thread-safe.
101130func (h Handle ) Delete () {
102- if h > MaxHandle || handles [h ] == & noHandle {
103- panic ("plugin-sdk-go/cgo: misuse of an invalid Handle" )
131+ if h > MaxHandle || atomic . LoadPointer ( & handles [h ]) == noHandle {
132+ panic (fmt . Sprintf ( "plugin-sdk-go/cgo: misuse (delete) of an invalid Handle %d" , h ) )
104133 }
105- handles [h ] = & noHandle
134+ atomic . StorePointer ( & handles [h ], noHandle )
106135}
107136
108137func resetHandles () {
109138 for i := 0 ; i <= MaxHandle ; i ++ {
110- handles [i ] = & noHandle
139+ atomic . StorePointer ( & handles [i ], noHandle )
111140 }
112141}
0 commit comments