blob: 1d5ff088c6388d43766269578decc9324eea9b6f [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 darwin,ios
package util
import (
"fmt"
"reflect"
"sync"
"sync/atomic"
)
import "C"
// 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() using the returned sequence id.
func GoNewRef(valptr interface{}) uint64 {
if !IsPointer(valptr) {
panic(fmt.Sprintf("must pass pointer value to goRef; instead got %v", valptr))
}
return goRefs.newRef(valptr)
}
// Increments the reference count to a previously existing id pointing to a value.
func GoRef(id uint64) {
goRefs.ref(id)
}
// Removes a previously added reference to the value addressed by the
// sequence id. If the value hasn't been ref-ed (a bug?), this unref will
// be a no-op.
func GoUnref(id uint64) {
goRefs.unref(id)
}
func GoGetRef(id uint64) interface{} {
return goRefs.get(id)
}
// 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
}
// Return 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()
}
// 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 Swift code. The only purpose of this store
// is to prevent Go runtime from garbage collecting those instances.
var goRefs = newSafeRefCounter()
var lastId uint64 = 0
type refData struct {
instance interface{}
count int
}
// Returns a new instance of a thread-safe reference counter.
func newSafeRefCounter() *safeRefCounter {
return &safeRefCounter{
refs: make(map[uint64]*refData),
}
}
// safeRefCounter is a thread-safe reference counter.
type safeRefCounter struct {
lock sync.Mutex
refs map[uint64]*refData
}
// Increment the reference count to the given valptr.
func (c *safeRefCounter) ref(id uint64) {
c.lock.Lock()
defer c.lock.Unlock()
ref, ok := c.refs[id]
if !ok {
panic(fmt.Sprintf("Refing id %d that doesn't exist", id))
} else {
ref.count++
}
}
// Given a valptr, store that into our map and return the associated handle for Swift
func (c *safeRefCounter) newRef(valptr interface{}) uint64 {
// p := PtrValue(valptr)
c.lock.Lock()
defer c.lock.Unlock()
id := atomic.AddUint64(&lastId, 1)
c.refs[id] = &refData{
instance: valptr,
count: 1,
}
return id
}
// Decrement the reference count of the valptr asssociated with the handle, returning
// the new reference count value and deleting the assocation if it hits 0.
func (c *safeRefCounter) unref(id uint64) int {
c.lock.Lock()
defer c.lock.Unlock()
ref, ok := c.refs[id]
if !ok {
panic(fmt.Sprintf("Unrefing id %d that hasn't been refed before", id))
}
count := ref.count
if count == 0 {
panic(fmt.Sprintf("Ref count for id %d is zero", id))
}
if count > 1 {
ref.count--
return ref.count
}
delete(c.refs, id)
return 0
}
// Given a handle, return the associated go object
func (c *safeRefCounter) get(id uint64) interface{} {
c.lock.Lock()
defer c.lock.Unlock()
ref, ok := c.refs[id]
if !ok {
panic(fmt.Sprintf("Trying to get id %d that doesn't exist", id))
} else {
return ref.instance
}
}