// 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) }