github.com/F4RD1N/gomobile@v1.0.1/bind/seq/ref.go (about) 1 // Copyright 2014 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package seq 6 7 //#cgo LDFLAGS: -llog 8 //#include <android/log.h> 9 //#include <string.h> 10 //import "C" 11 12 import ( 13 "fmt" 14 "runtime" 15 "sync" 16 ) 17 18 type countedObj struct { 19 obj interface{} 20 cnt int32 21 } 22 23 // also known to bind/java/Seq.java and bind/objc/seq_darwin.m 24 const NullRefNum = 41 25 26 // refs stores Go objects that have been passed to another language. 27 var refs struct { 28 sync.Mutex 29 next int32 // next reference number to use for Go object, always negative 30 refs map[interface{}]int32 31 objs map[int32]countedObj 32 } 33 34 func init() { 35 refs.Lock() 36 refs.next = -24 // Go objects get negative reference numbers. Arbitrary starting point. 37 refs.refs = make(map[interface{}]int32) 38 refs.objs = make(map[int32]countedObj) 39 refs.Unlock() 40 } 41 42 // A Ref represents a Java or Go object passed across the language 43 // boundary. 44 type Ref struct { 45 Bind_Num int32 46 } 47 48 type proxy interface { 49 // Use a strange name and hope that user code does not implement it 50 Bind_proxy_refnum__() int32 51 } 52 53 // ToRefNum increments the reference count for an object and 54 // returns its refnum. 55 func ToRefNum(obj interface{}) int32 { 56 // We don't track foreign objects, so if obj is a proxy 57 // return its refnum. 58 if r, ok := obj.(proxy); ok { 59 refnum := r.Bind_proxy_refnum__() 60 if refnum <= 0 { 61 panic(fmt.Errorf("seq: proxy contained invalid Go refnum: %d", refnum)) 62 } 63 return refnum 64 } 65 refs.Lock() 66 num := refs.refs[obj] 67 if num != 0 { 68 s := refs.objs[num] 69 refs.objs[num] = countedObj{s.obj, s.cnt + 1} 70 } else { 71 num = refs.next 72 refs.next-- 73 if refs.next > 0 { 74 panic("refs.next underflow") 75 } 76 refs.refs[obj] = num 77 refs.objs[num] = countedObj{obj, 1} 78 } 79 refs.Unlock() 80 81 return int32(num) 82 } 83 84 // FromRefNum returns the Ref for a refnum. If the refnum specifies a 85 // foreign object, a finalizer is set to track its lifetime. 86 func FromRefNum(num int32) *Ref { 87 if num == NullRefNum { 88 return nil 89 } 90 ref := &Ref{num} 91 if num > 0 { 92 // This is a foreign object reference. 93 // Track its lifetime with a finalizer. 94 runtime.SetFinalizer(ref, FinalizeRef) 95 } 96 97 return ref 98 } 99 100 // Bind_IncNum increments the foreign reference count and 101 // return the refnum. 102 func (r *Ref) Bind_IncNum() int32 { 103 refnum := r.Bind_Num 104 IncForeignRef(refnum) 105 // Make sure this reference is not finalized before 106 // the foreign reference count is incremented. 107 runtime.KeepAlive(r) 108 return refnum 109 } 110 111 // Get returns the underlying object. 112 func (r *Ref) Get() interface{} { 113 refnum := r.Bind_Num 114 refs.Lock() 115 o, ok := refs.objs[refnum] 116 refs.Unlock() 117 if !ok { 118 panic(fmt.Sprintf("unknown ref %d", refnum)) 119 } 120 // This is a Go reference and its refnum was incremented 121 // before crossing the language barrier. 122 Delete(refnum) 123 return o.obj 124 } 125 126 // Inc increments the reference count for a refnum. Called from Bind_proxy_refnum 127 // functions. 128 func Inc(num int32) { 129 refs.Lock() 130 o, ok := refs.objs[num] 131 if !ok { 132 panic(fmt.Sprintf("seq.Inc: unknown refnum: %d", num)) 133 } 134 refs.objs[num] = countedObj{o.obj, o.cnt + 1} 135 refs.Unlock() 136 } 137 138 // Delete decrements the reference count and removes the pinned object 139 // from the object map when the reference count becomes zero. 140 func Delete(num int32) { 141 refs.Lock() 142 defer refs.Unlock() 143 o, ok := refs.objs[num] 144 if !ok { 145 panic(fmt.Sprintf("seq.Delete unknown refnum: %d", num)) 146 } 147 if o.cnt <= 1 { 148 delete(refs.objs, num) 149 delete(refs.refs, o.obj) 150 } else { 151 refs.objs[num] = countedObj{o.obj, o.cnt - 1} 152 } 153 }