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 }