blob: 2b1943374ab9548f501a145f9b672789641248ac [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"
"reflect"
"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().
func NewGlobalRef(env Env, obj Object) Object {
return Object(uintptr(unsafe.Pointer(C.NewGlobalRef(env.value(), obj.value()))))
}
// DeleteGlobalRef deletes the global reference pointed to by obj.
func DeleteGlobalRef(env Env, obj Object) {
C.DeleteGlobalRef(env.value(), obj.value())
}
// NewLocalRef 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.
func NewLocalRef(env Env, obj Object) Object {
return Object(uintptr(unsafe.Pointer(C.NewLocalRef(env.value(), obj.value()))))
}
// DeleteLocalRef deletes the local reference pointed to by obj.
func DeleteLocalRef(env Env, obj Object) {
C.DeleteLocalRef(env.value(), obj.value())
}
// 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 {
v := reflect.ValueOf(val)
return v.Kind() == reflect.Ptr || v.Kind() == reflect.UnsafePointer
}
// 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.Type()))
}
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.Type()))
}
return v.Elem().Interface()
}
// NativePtr returns the value of the provided Go pointer as an unsafe.Pointer.
// This function should only be used for converting Go pointers that have been
// passed in to Java and then back into Go, and are of (local-package) type
// C.jlong.
func NativePtr(goPtr interface{}) unsafe.Pointer {
v := reflect.ValueOf(goPtr)
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++
}
}
// unref decreases the reference count of the given valptr by 1, returning
// the new reference count value.
func (c *safeRefCounter) unref(valptr interface{}) int {
c.lock.Lock()
defer c.lock.Unlock()
p := PtrValue(valptr)
ref, ok := c.refs[p]
if !ok {
panic(fmt.Sprintf("Unrefing pointer %d of type %T that hasn't been refed before", int64(p), valptr))
}
count := ref.count
if count == 0 {
panic(fmt.Sprintf("Ref count for pointer %d of type %T is zero", int64(p), valptr))
}
if count > 1 {
ref.count--
return ref.count
}
delete(c.refs, p)
return 0
}