github.com/SahandAslani/gomobile@v0.0.0-20210909130135-2cb2d44c09b2/bind/java/Seq.java (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 go;
     6  
     7  import android.content.Context;
     8  
     9  import java.lang.ref.PhantomReference;
    10  import java.lang.ref.Reference;
    11  import java.lang.ref.ReferenceQueue;
    12  import java.util.Arrays;
    13  import java.util.Collection;
    14  import java.util.Collections;
    15  import java.util.IdentityHashMap;
    16  import java.util.HashSet;
    17  import java.util.Set;
    18  import java.util.logging.Logger;
    19  
    20  import go.Universe;
    21  
    22  // Seq is a sequence of machine-dependent encoded values.
    23  // Used by automatically generated language bindings to talk to Go.
    24  public class Seq {
    25  	private static Logger log = Logger.getLogger("GoSeq");
    26  
    27  	// also known to bind/seq/ref.go and bind/objc/seq_darwin.m
    28  	private static final int NULL_REFNUM = 41;
    29  
    30  	// use single Ref for null Object
    31  	public static final Ref nullRef = new Ref(NULL_REFNUM, null);
    32  
    33  	// The singleton GoRefQueue
    34  	private static final GoRefQueue goRefQueue = new GoRefQueue();
    35  
    36  	static {
    37          System.out.println("sahandddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd");
    38  		System.loadLibrary("gojni");
    39          System.out.println("sahandddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd");
    40  		init();
    41  		Universe.touch();
    42  	}
    43  
    44  	// setContext sets the context in the go-library to be used in RunOnJvm.
    45  	public static void setContext(Context context) {
    46  		setContext((java.lang.Object)context);
    47  	}
    48  
    49  	private static native void init();
    50  
    51  	// Empty method to run class initializer
    52  	public static void touch() {}
    53  
    54  	private Seq() {
    55  	}
    56  
    57  	// ctx is an android.context.Context.
    58  	static native void setContext(java.lang.Object ctx);
    59  
    60  	public static void incRefnum(int refnum) {
    61  		tracker.incRefnum(refnum);
    62  	}
    63  
    64  	// incRef increments the reference count of Java objects.
    65  	// For proxies for Go objects, it calls into the Proxy method
    66  	// incRefnum() to make sure the Go reference count is positive
    67  	// even if the Proxy is garbage collected and its Ref is finalized.
    68  	public static int incRef(Object o) {
    69  		return tracker.inc(o);
    70  	}
    71  
    72  	public static int incGoObjectRef(GoObject o) {
    73  		return o.incRefnum();
    74  	}
    75  
    76  	// trackGoRef tracks a Go reference and decrements its refcount
    77  	// when the given GoObject wrapper is garbage collected.
    78  	//
    79  	// TODO(crawshaw): We could cut down allocations for frequently
    80  	// sent Go objects by maintaining a map to weak references. This
    81  	// however, would require allocating two objects per reference
    82  	// instead of one. It also introduces weak references, the bane
    83  	// of any Java debugging session.
    84  	//
    85  	// When we have real code, examine the tradeoffs.
    86  	public static void trackGoRef(int refnum, GoObject obj) {
    87  		if (refnum > 0) {
    88  			throw new RuntimeException("trackGoRef called with Java refnum " + refnum);
    89  		}
    90  		goRefQueue.track(refnum, obj);
    91  	}
    92  
    93  	public static Ref getRef(int refnum) {
    94  		return tracker.get(refnum);
    95  	}
    96  
    97  	// Increment the Go reference count before sending over a refnum.
    98  	// The ref parameter is only used to make sure the referenced
    99  	// object is not garbage collected before Go increments the
   100  	// count. It's the equivalent of Go's runtime.KeepAlive.
   101  	public static native void incGoRef(int refnum, GoObject ref);
   102  
   103  	// Informs the Go ref tracker that Java is done with this refnum.
   104  	static native void destroyRef(int refnum);
   105  
   106  	// decRef is called from seq.FinalizeRef
   107  	static void decRef(int refnum) {
   108  		tracker.dec(refnum);
   109  	}
   110  
   111  	// A GoObject is a Java class implemented in Go. When a GoObject
   112  	// is passed to Go, it is wrapped in a Go proxy, to make it behave
   113  	// the same as passing a regular Java class.
   114  	public interface GoObject {
   115  		// Increment refcount and return the refnum of the proxy.
   116  		//
   117  		// The Go reference count need to be bumped while the
   118  		// refnum is passed to Go, to avoid finalizing and
   119  		// invalidating it before being translated on the Go side.
   120  		int incRefnum();
   121  	}
   122  	// A Proxy is a Java object that proxies a Go object. Proxies, unlike
   123  	// GoObjects, are unwrapped to their Go counterpart when deserialized
   124  	// in Go.
   125  	public interface Proxy extends GoObject {}
   126  
   127  	// A Ref represents an instance of a Java object passed back and forth
   128  	// across the language boundary.
   129  	public static final class Ref {
   130  		public final int refnum;
   131  
   132  		private int refcnt;  // Track how many times sent to Go.
   133  
   134  		public final Object obj;  // The referenced Java obj.
   135  
   136  		Ref(int refnum, Object o) {
   137  			if (refnum < 0) {
   138  				throw new RuntimeException("Ref instantiated with a Go refnum " + refnum);
   139  			}
   140  			this.refnum = refnum;
   141  			this.refcnt = 0;
   142  			this.obj = o;
   143  		}
   144  
   145  		void inc() {
   146  			// Count how many times this ref's Java object is passed to Go.
   147  			if (refcnt == Integer.MAX_VALUE) {
   148  				throw new RuntimeException("refnum " + refnum + " overflow");
   149  			}
   150  			refcnt++;
   151  		}
   152  	}
   153  
   154  	static final RefTracker tracker = new RefTracker();
   155  
   156  	static final class RefTracker {
   157  		private static final int REF_OFFSET = 42;
   158  
   159  		// Next Java object reference number.
   160  		//
   161  		// Reference numbers are positive for Java objects,
   162  		// and start, arbitrarily at a different offset to Go
   163  		// to make debugging by reading Seq hex a little easier.
   164  		private int next = REF_OFFSET; // next Java object ref
   165  
   166  		// Java objects that have been passed to Go. refnum -> Ref
   167  		// The Ref obj field is non-null.
   168  		// This map pins Java objects so they don't get GCed while the
   169  		// only reference to them is held by Go code.
   170  		private final RefMap javaObjs = new RefMap();
   171  
   172  		// Java objects to refnum
   173  		private final IdentityHashMap<Object, Integer> javaRefs = new IdentityHashMap<>();
   174  
   175  		// inc increments the reference count of a Java object when it
   176  		// is sent to Go. inc returns the refnum for the object.
   177  		synchronized int inc(Object o) {
   178  			if (o == null) {
   179  				return NULL_REFNUM;
   180  			}
   181  			if (o instanceof Proxy) {
   182  				return ((Proxy)o).incRefnum();
   183  			}
   184  			Integer refnumObj = javaRefs.get(o);
   185  			if (refnumObj == null) {
   186  				if (next == Integer.MAX_VALUE) {
   187  					throw new RuntimeException("createRef overflow for " + o);
   188  				}
   189  				refnumObj = next++;
   190  				javaRefs.put(o, refnumObj);
   191  			}
   192  			int refnum = refnumObj;
   193  			Ref ref = javaObjs.get(refnum);
   194  			if (ref == null) {
   195  				ref = new Ref(refnum, o);
   196  				javaObjs.put(refnum, ref);
   197  			}
   198  			ref.inc();
   199  			return refnum;
   200  		}
   201  
   202  		synchronized void incRefnum(int refnum) {
   203  			Ref ref = javaObjs.get(refnum);
   204  			if (ref == null) {
   205  				throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
   206  			}
   207  			ref.inc();
   208  		}
   209  
   210  		// dec decrements the reference count of a Java object when
   211  		// Go signals a corresponding proxy object is finalized.
   212  		// If the count reaches zero, the Java object is removed
   213  		// from the javaObjs map.
   214  		synchronized void dec(int refnum) {
   215  			if (refnum <= 0) {
   216  				// We don't keep track of the Go object.
   217  				// This must not happen.
   218  				log.severe("dec request for Go object "+ refnum);
   219  				return;
   220  			}
   221  			if (refnum == Seq.nullRef.refnum) {
   222  				return;
   223  			}
   224  			// Java objects are removed on request of Go.
   225  			Ref obj = javaObjs.get(refnum);
   226  			if (obj == null) {
   227  				throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
   228  			}
   229  			obj.refcnt--;
   230  			if (obj.refcnt <= 0) {
   231  				javaObjs.remove(refnum);
   232  				javaRefs.remove(obj.obj);
   233  			}
   234  		}
   235  
   236  		// get returns an existing Ref to a Java object.
   237  		synchronized Ref get(int refnum) {
   238  			if (refnum < 0) {
   239  				throw new RuntimeException("ref called with Go refnum " + refnum);
   240  			}
   241  			if (refnum == NULL_REFNUM) {
   242  				return nullRef;
   243  			}
   244  			Ref ref = javaObjs.get(refnum);
   245  			if (ref == null) {
   246  				throw new RuntimeException("unknown java Ref: "+refnum);
   247  			}
   248  			return ref;
   249  		}
   250  	}
   251  
   252  	// GoRefQueue is a queue of GoRefs that are no longer live. An internal thread
   253  	// processes the queue and decrement the reference count on the Go side.
   254  	static class GoRefQueue extends ReferenceQueue<GoObject> {
   255  		// The set of tracked GoRefs. If we don't hold on to the GoRef instances, the Java GC
   256  		// will not add them to the queue when their referents are reclaimed.
   257  		private final Collection<GoRef> refs = Collections.synchronizedCollection(new HashSet<GoRef>());
   258  
   259  		void track(int refnum, GoObject obj) {
   260  			refs.add(new GoRef(refnum, obj, this));
   261  		}
   262  
   263  		GoRefQueue() {
   264  			Thread daemon = new Thread(new Runnable() {
   265  				@Override public void run() {
   266  					while (true) {
   267  						try {
   268  							GoRef ref = (GoRef)remove();
   269  							refs.remove(ref);
   270  							destroyRef(ref.refnum);
   271  							ref.clear();
   272  						} catch (InterruptedException e) {
   273  							// Ignore
   274  						}
   275  					}
   276  				}
   277  			});
   278  			daemon.setDaemon(true);
   279  			daemon.setName("GoRefQueue Finalizer Thread");
   280  			daemon.start();
   281  		}
   282  	}
   283  
   284  	// A GoRef is a PhantomReference to a Java proxy for a Go object.
   285  	// GoRefs are enqueued to the singleton GoRefQueue when no longer live,
   286  	// so the corresponding reference count can be decremented.
   287  	static class GoRef extends PhantomReference<GoObject> {
   288  		final int refnum;
   289  
   290  		GoRef(int refnum, GoObject obj, GoRefQueue q) {
   291  			super(obj, q);
   292  			if (refnum > 0) {
   293  				throw new RuntimeException("GoRef instantiated with a Java refnum " + refnum);
   294  			}
   295  			this.refnum = refnum;
   296  		}
   297  	}
   298  
   299  	// RefMap is a mapping of integers to Ref objects.
   300  	//
   301  	// The integers can be sparse. In Go this would be a map[int]*Ref.
   302  	static final class RefMap {
   303  		private int next = 0;
   304  		private int live = 0;
   305  		private int[] keys = new int[16];
   306  		private Ref[] objs = new Ref[16];
   307  
   308  		RefMap() {}
   309  
   310  		Ref get(int key) {
   311  			int i = Arrays.binarySearch(keys, 0, next, key);
   312  			if (i >= 0) {
   313  				return objs[i];
   314  			}
   315  			return null;
   316  		}
   317  
   318  		void remove(int key) {
   319  			int i = Arrays.binarySearch(keys, 0, next, key);
   320  			if (i >= 0) {
   321  				if (objs[i] != null) {
   322  					objs[i] = null;
   323  					live--;
   324  				}
   325  			}
   326  		}
   327  
   328  		void put(int key, Ref obj) {
   329  			if (obj == null) {
   330  				throw new RuntimeException("put a null ref (with key "+key+")");
   331  			}
   332  			int i = Arrays.binarySearch(keys, 0, next, key);
   333  			if (i >= 0) {
   334  				if (objs[i] == null) {
   335  					objs[i] = obj;
   336  					live++;
   337  				}
   338  				if (objs[i] != obj) {
   339  					throw new RuntimeException("replacing an existing ref (with key "+key+")");
   340  				}
   341  				return;
   342  			}
   343  			if (next >= keys.length) {
   344  				grow();
   345  				i = Arrays.binarySearch(keys, 0, next, key);
   346  			}
   347  			i = ~i;
   348  			if (i < next) {
   349  				// Insert, shift everything afterwards down.
   350  				System.arraycopy(keys, i, keys, i+1, next-i);
   351  				System.arraycopy(objs, i, objs, i+1, next-i);
   352  			}
   353  			keys[i] = key;
   354  			objs[i] = obj;
   355  			live++;
   356  			next++;
   357  		}
   358  
   359  		private void grow() {
   360  			// Compact and (if necessary) grow backing store.
   361  			int[] newKeys;
   362  			Ref[] newObjs;
   363  			int len = 2*roundPow2(live);
   364  			if (len > keys.length) {
   365  				newKeys = new int[keys.length*2];
   366  				newObjs = new Ref[objs.length*2];
   367  			} else {
   368  				newKeys = keys;
   369  				newObjs = objs;
   370  			}
   371  
   372  			int j = 0;
   373  			for (int i = 0; i < keys.length; i++) {
   374  				if (objs[i] != null) {
   375  					newKeys[j] = keys[i];
   376  					newObjs[j] = objs[i];
   377  					j++;
   378  				}
   379  			}
   380  			for (int i = j; i < newKeys.length; i++) {
   381  				newKeys[i] = 0;
   382  				newObjs[i] = null;
   383  			}
   384  
   385  			keys = newKeys;
   386  			objs = newObjs;
   387  			next = j;
   388  
   389  			if (live != next) {
   390  				throw new RuntimeException("bad state: live="+live+", next="+next);
   391  			}
   392  		}
   393  
   394  		private static int roundPow2(int x) {
   395  			int p = 1;
   396  			while (p < x) {
   397  				p *= 2;
   398  			}
   399  			return p;
   400  		}
   401  	}
   402  }