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