blob: 65a4f0ee3eebd27dbf52a3169afaa2e83efa0646 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build java android
package util
import (
"fmt"
"log"
"reflect"
"runtime/debug"
"sync"
"unsafe"
)
// #include "jni_wrapper.h"
import "C"
// NewGlobalRef creates a new global reference to the object referred to by the
// obj argument. The obj argument may be a global or local reference. Global
// references must be explicitly disposed of by calling DeleteGlobalRef().
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
func NewGlobalRef(env, obj interface{}) unsafe.Pointer {
jEnv := getEnv(env)
jObj := getObject(obj)
return unsafe.Pointer(C.NewGlobalRef(jEnv, jObj))
}
// DeleteGlobalRef deletes the global reference pointed to by obj.
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
func DeleteGlobalRef(env, obj interface{}) {
jEnv := getEnv(env)
jObj := getObject(obj)
C.DeleteGlobalRef(jEnv, jObj)
}
// Creates a new local reference that refers to the same object as obj. The
// given obj may be a global or local reference. Returns null if ref refers
// to null.
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
func NewLocalRef(env, obj interface{}) unsafe.Pointer {
jEnv := getEnv(env)
jObj := getObject(obj)
return unsafe.Pointer(C.NewLocalRef(jEnv, jObj))
}
// IsNull returns true iff the provided object is not null.
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
func IsNull(obj interface{}) bool {
return getObject(obj) == nil
}
// GoRef creates a new reference to the value addressed by the provided pointer.
// The value will remain referenced until it is explicitly unreferenced using
// goUnref().
func GoRef(valptr interface{}) {
if !IsPointer(valptr) {
panic("must pass pointer value to goRef")
}
goRefs.ref(valptr)
}
// GoUnref removes a previously added reference to the value addressed by the
// provided pointer. If the value hasn't been ref-ed (a bug?), this unref will
// be a no-op.
func GoUnref(valptr interface{}) {
if !IsPointer(valptr) {
panic("must pass pointer value to goUnref")
}
goRefs.unref(valptr)
}
// IsPointer returns true iff the provided value is a pointer.
func IsPointer(val interface{}) bool {
if _, ok := val.(unsafe.Pointer); ok {
return true
}
return reflect.ValueOf(val).Kind() == reflect.Ptr
}
// PtrValue returns the value of the pointer as a uintptr.
func PtrValue(ptr interface{}) uintptr {
v := reflect.ValueOf(ptr)
if v.Kind() != reflect.Ptr && v.Kind() != reflect.UnsafePointer {
panic(fmt.Sprintf("must pass pointer value to PtrValue, was %v", v.Kind()))
}
return v.Pointer()
}
// DerefOrDie dereferences the provided (pointer) value, or panic-s if the value
// isn't of pointer type.
func DerefOrDie(i interface{}) interface{} {
v := reflect.ValueOf(i)
if v.Kind() != reflect.Ptr {
panic(fmt.Sprintf("want reflect.Ptr value for %v, have %v", i, v.Kind()))
}
return v.Elem().Interface()
}
// Ptr returns the value of the provided Java pointer (of type C.jlong) as an
// unsafe.Pointer.
// NOTE: Because CGO creates package-local types and because this method may be
// invoked from a different package, Java types are passed in an empty interface
// and then cast into their package local types.
func Ptr(jPtr interface{}) unsafe.Pointer {
v := reflect.ValueOf(jPtr)
return unsafe.Pointer(uintptr(v.Int()))
}
// goRefs stores references to instances of various Go types, namely instances
// that are referenced only by the Java code. The only purpose of this store
// is to prevent Go runtime from garbage collecting those instances.
var goRefs = newSafeRefCounter()
type refData struct {
instance interface{}
count int
}
// newSafeRefCounter returns a new instance of a thread-safe reference counter.
func newSafeRefCounter() *safeRefCounter {
return &safeRefCounter{
refs: make(map[uintptr]*refData),
}
}
// safeRefCounter is a thread-safe reference counter.
type safeRefCounter struct {
lock sync.Mutex
refs map[uintptr]*refData
}
// ref increases the reference count to the given valptr by 1.
func (c *safeRefCounter) ref(valptr interface{}) {
p := PtrValue(valptr)
c.lock.Lock()
defer c.lock.Unlock()
ref, ok := c.refs[p]
if !ok {
c.refs[p] = &refData{
instance: valptr,
count: 1,
}
} else {
ref.count++
}
}
func (c *safeRefCounter) unref(valptr interface{}) {
c.lock.Lock()
defer c.lock.Unlock()
p := PtrValue(valptr)
ref, ok := c.refs[p]
if !ok {
log.Printf("Unrefing pointer %d of type %T that hasn't been refed before, stack: %s", int64(p), valptr, string(debug.Stack()))
return
}
count := ref.count
if count == 0 {
log.Printf("Ref count for pointer %d of type %T is zero: that shouldn't happen, stack: %s", int64(p), valptr, string(debug.Stack()))
return
}
if count > 1 {
ref.count--
return
}
delete(c.refs, p)
}