mirror of https://github.com/go-python/gopy.git
211 lines
5.0 KiB
Go
211 lines
5.0 KiB
Go
// Copyright 2019 The go-python Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// Package gopyh provides the variable handle manager for gopy.
|
|
// The handles map can NOT be globally shared in C because it
|
|
// must use interface{} values that can change location via GC.
|
|
// In effect, each gopy package must be thought of as a completely
|
|
// separate Go instance, and there can be NO sharing of anything
|
|
// between them, because they fundamentally live in different .so
|
|
// libraries.
|
|
// Thus, we must ensure that all handles used within a package
|
|
// are registered within that same package -- this means python
|
|
// users typically will import a single package, which exports
|
|
// all the relevant functionality as needed. Any further packages
|
|
// cannot share anything other than basic types (int, string etc).
|
|
package gopyh
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"strconv"
|
|
"sync"
|
|
)
|
|
|
|
// GoHandle is the type for the handle
|
|
type GoHandle int64
|
|
type CGoHandle int64
|
|
|
|
// --- variable handles: all pointers managed via handles ---
|
|
|
|
var (
|
|
mu sync.RWMutex
|
|
ctr int64
|
|
handles map[GoHandle]interface{}
|
|
counts map[GoHandle]int64
|
|
)
|
|
|
|
// IfaceIsNil returns true if interface or value represented by interface is nil
|
|
func IfaceIsNil(it interface{}) bool {
|
|
if it == nil {
|
|
return true
|
|
}
|
|
v := reflect.ValueOf(it)
|
|
vk := v.Kind()
|
|
if vk == reflect.Ptr || vk == reflect.Interface || vk == reflect.Map || vk == reflect.Slice || vk == reflect.Func || vk == reflect.Chan {
|
|
return v.IsNil()
|
|
}
|
|
return false
|
|
}
|
|
|
|
// NonPtrValue returns the non-pointer underlying value
|
|
func NonPtrValue(v reflect.Value) reflect.Value {
|
|
for v.Kind() == reflect.Ptr {
|
|
v = v.Elem()
|
|
}
|
|
return v
|
|
}
|
|
|
|
// PtrValue returns the pointer version (Addr()) of the underlying value if
|
|
// the value is not already a Ptr
|
|
func PtrValue(v reflect.Value) reflect.Value {
|
|
if v.CanAddr() && v.Kind() != reflect.Ptr {
|
|
v = v.Addr()
|
|
}
|
|
return v
|
|
}
|
|
|
|
// Embed returns the embedded struct (in first field only) of given type within given struct
|
|
func Embed(stru interface{}, embed reflect.Type) interface{} {
|
|
if IfaceIsNil(stru) {
|
|
return nil
|
|
}
|
|
v := NonPtrValue(reflect.ValueOf(stru))
|
|
typ := v.Type()
|
|
if typ == embed {
|
|
return PtrValue(v).Interface()
|
|
}
|
|
if typ.NumField() == 0 {
|
|
return nil
|
|
}
|
|
f := typ.Field(0)
|
|
if f.Type.Kind() == reflect.Struct && f.Anonymous { // anon only avail on StructField fm typ
|
|
vf := v.Field(0)
|
|
vfpi := PtrValue(vf).Interface()
|
|
if f.Type == embed {
|
|
return vfpi
|
|
}
|
|
rv := Embed(vfpi, embed)
|
|
if rv != nil {
|
|
return rv
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var (
|
|
trace = false
|
|
)
|
|
|
|
func init() {
|
|
if len(os.Getenv("GOPY_HANDLE_TRACE")) > 0 {
|
|
trace = true
|
|
}
|
|
}
|
|
|
|
// Register registers a new variable instance.
|
|
func Register(typnm string, ifc interface{}) CGoHandle {
|
|
if IfaceIsNil(ifc) {
|
|
return -1
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if handles == nil {
|
|
handles = make(map[GoHandle]interface{})
|
|
counts = make(map[GoHandle]int64)
|
|
}
|
|
ctr++
|
|
hc := ctr
|
|
ghc := GoHandle(hc)
|
|
handles[ghc] = ifc
|
|
counts[ghc] = 0
|
|
if trace {
|
|
fmt.Printf("gopy Registered: %s %v %d\n", typnm, ifc, hc)
|
|
}
|
|
return CGoHandle(hc)
|
|
}
|
|
|
|
// DecRef decrements the reference count for the specified handle
|
|
// and removes it if the reference count goes to zero.
|
|
func DecRef(handle CGoHandle) {
|
|
if handle < 1 {
|
|
return
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
if handles == nil {
|
|
return
|
|
}
|
|
ghc := GoHandle(handle)
|
|
if _, exists := handles[ghc]; !exists {
|
|
return
|
|
}
|
|
counts[ghc]--
|
|
switch cnt := counts[ghc]; {
|
|
case cnt == 0:
|
|
delete(counts, ghc)
|
|
delete(handles, ghc)
|
|
if trace {
|
|
fmt.Printf("gopy DecRef: %d\n", handle)
|
|
}
|
|
case cnt < 0:
|
|
panic(fmt.Sprintf("gopy DecRef ref count %v for handle: %v, ifc %v", cnt, ghc, handles[ghc]))
|
|
default:
|
|
if trace {
|
|
fmt.Printf("gopy DecRef: %d: %d\n", handle, cnt)
|
|
}
|
|
}
|
|
}
|
|
|
|
// IncRef increments the reference count for the specified handle.
|
|
func IncRef(handle CGoHandle) {
|
|
if handle < 1 {
|
|
return
|
|
}
|
|
mu.Lock()
|
|
defer mu.Unlock()
|
|
ghc := GoHandle(handle)
|
|
if _, exists := counts[ghc]; exists {
|
|
counts[ghc]++
|
|
if trace {
|
|
fmt.Printf("gopy IncRef: %d: %d\n", handle, counts[ghc])
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// VarFromHandle gets variable from handle string.
|
|
// Reports error to python but does not return it,
|
|
// for use in inline calls
|
|
func VarFromHandle(h CGoHandle, typnm string) interface{} {
|
|
v, _ := VarFromHandleTry(h, typnm)
|
|
return v
|
|
}
|
|
|
|
// VarFromHandleTry version returns the error explicitly,
|
|
// for use when error can be processed
|
|
func VarFromHandleTry(h CGoHandle, typnm string) (interface{}, error) {
|
|
if h < 1 {
|
|
return nil, fmt.Errorf("gopy: nil handle")
|
|
}
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
v, has := handles[GoHandle(h)]
|
|
if !has {
|
|
err := fmt.Errorf("gopy: variable handle not registered: " + strconv.FormatInt(int64(h), 10))
|
|
// TODO: need to get access to this:
|
|
// C.PyErr_SetString(C.PyExc_TypeError, C.CString(err.Error()))
|
|
return nil, err
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
// NumHandles returns the number of handles in use.
|
|
func NumHandles() int {
|
|
mu.RLock()
|
|
defer mu.RUnlock()
|
|
return len(handles)
|
|
}
|