blob: 7c154cc57c8d666fb63be846a43a7948629f788d [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 {
if obj.IsNull() {
return obj
}
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) {
if !obj.IsNull() {
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.
func NewLocalRef(env Env, obj Object) Object {
if obj.IsNull() {
return obj
}
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) {
if !obj.IsNull() {
C.DeleteLocalRef(env.value(), obj.value())
}
}
// IsGlobalRef returns true iff the reference pointed to by obj is a global reference.
func IsGlobalRef(env Env, obj Object) bool {
return !obj.IsNull() && C.GetObjectRefType(env.value(), obj.value()) == C.JNIGlobalRefType
}
// IsLocalRef returns true iff the reference pointed to by obj is a local reference.
func IsLocalRef(env Env, obj Object) bool {
return obj.IsNull() || C.GetObjectRefType(env.value(), obj.value()) == C.JNILocalRefType
}
// GoNewRef creates a new reference for the given Go pointer, setting its
// reference count to 1. The Go pointer isn't garbage collected as long as
// the reference count remains greater than zero. The reference counts can
// be modified using GoIncRef and GoDecRef functions below.
//
// Note that it is legal to create multiple references for the same Go pointer:
// the Go pointer will not be garbage collected as long as some reference's
// ref count is greater than zero.
func GoNewRef(valptr interface{}) Ref {
if !IsPointer(valptr) {
panic(fmt.Sprintf("Must pass pointer value to GoNewRef; instead got %v of type %T", valptr, valptr))
}
return goRefs.newRef(valptr)
}
// GoIncRef increments the reference count for the given reference by 1.
func GoIncRef(ref Ref) {
goRefs.incRef(ref)
}
// GoDecRef decrements the reference count for the given reference by 1.
func GoDecRef(ref Ref) {
goRefs.decRef(ref)
}
// GoRefValue returns the Go pointer associated with the given reference
// as an unsafe.Pointer (so that it can be easily cast into its right type).
func GoRefValue(ref Ref) unsafe.Pointer {
valptr := goRefs.getVal(ref)
v := reflect.ValueOf(valptr)
if v.Kind() != reflect.Ptr && v.Kind() != reflect.UnsafePointer {
panic(fmt.Sprintf("must pass pointer value to PtrValue, was %v ", v.Type()))
}
return unsafe.Pointer(v.Pointer())
}
// 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
}
// 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()
}
// 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{
seq: 1, // must be > 0, as 0 is a special value
refs: make(map[Ref]*refData),
}
}
// safeRefCounter is a thread-safe reference counter.
type safeRefCounter struct {
lock sync.RWMutex
seq Ref
refs map[Ref]*refData
}
// newRef creates a new reference to a given Go pointer and sets its ref count to 1.
func (c *safeRefCounter) newRef(valptr interface{}) Ref {
c.lock.Lock()
defer c.lock.Unlock()
ref := c.seq
c.seq++
c.refs[ref] = &refData{
instance: valptr,
count: 1,
}
return ref
}
// ref increases the reference count for the given reference by 1.
func (c *safeRefCounter) incRef(ref Ref) {
c.lock.Lock()
defer c.lock.Unlock()
data, ok := c.refs[ref]
if !ok {
panic(fmt.Sprintf("Reference %d doesn't exist", ref))
}
data.count++
}
// unref decreases the reference count for the given reference by 1.
func (c *safeRefCounter) decRef(ref Ref) {
c.lock.Lock()
defer c.lock.Unlock()
data, ok := c.refs[ref]
if !ok {
panic(fmt.Sprintf("Reference %d doesn't exist", ref))
}
if data.count == 0 {
panic(fmt.Sprintf("Reference %d exists, but doesn't have a count of 0.", ref))
}
data.count--
if data.count == 0 {
delete(c.refs, ref)
}
}
// getVal returns the value for the given reference.
func (c *safeRefCounter) getVal(ref Ref) interface{} {
c.lock.RLock()
defer c.lock.RUnlock()
data, ok := c.refs[ref]
if !ok {
panic(fmt.Sprintf("Reference %d doesn't exist.", ref))
}
return data.instance
}