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  }