github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/mobile/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 java.util.Arrays; 8 import java.util.concurrent.ExecutorService; 9 import java.util.concurrent.Executors; 10 import java.util.logging.Logger; 11 12 // Seq is a sequence of machine-dependent encoded values. 13 // Used by automatically generated language bindings to talk to Go. 14 public class Seq { 15 private static Logger log = Logger.getLogger("GoSeq"); 16 17 static { 18 // Look for the shim class auto-generated by gomobile bind. 19 // Its only purpose is to call System.loadLibrary. 20 try { 21 Class loadJNI = Class.forName("go.LoadJNI"); 22 setContext(loadJNI.getDeclaredField("ctx").get(null)); 23 } catch (ClassNotFoundException e) { 24 // Ignore, assume the user will load JNI for it. 25 log.warning("LoadJNI class not found"); 26 } catch (NoSuchFieldException e) { 27 log.severe("LoadJNI class missing field: " + e); 28 } catch (IllegalAccessException e) { 29 log.severe("LoadJNI class bad field: " + e); 30 } 31 32 initSeq(); 33 new Thread("GoSeq") { 34 public void run() { Seq.receive(); } 35 }.start(); 36 } 37 38 @SuppressWarnings("UnusedDeclaration") 39 private long memptr; // holds C-allocated pointer 40 41 public Seq() { 42 ensure(64); 43 } 44 45 // ctx is an android.context.Context. 46 static native void setContext(java.lang.Object ctx); 47 48 // Ensure that at least size bytes can be written to the Seq. 49 // Any existing data in the buffer is preserved. 50 public native void ensure(int size); 51 52 // Moves the internal buffer offset back to zero. 53 // Length and contents are maintained. Data can be read after a reset. 54 public native void resetOffset(); 55 56 public native void log(String label); 57 58 public native boolean readBool(); 59 public native byte readInt8(); 60 public native short readInt16(); 61 public native int readInt32(); 62 public native long readInt64(); 63 public long readInt() { return readInt64(); } 64 65 public native float readFloat32(); 66 public native double readFloat64(); 67 public native String readUTF16(); 68 public String readString() { return readUTF16(); } 69 public native byte[] readByteArray(); 70 71 public native void writeBool(boolean v); 72 public native void writeInt8(byte v); 73 public native void writeInt16(short v); 74 public native void writeInt32(int v); 75 public native void writeInt64(long v); 76 public void writeInt(long v) { writeInt64(v); } 77 78 public native void writeFloat32(float v); 79 public native void writeFloat64(double v); 80 public native void writeUTF16(String v); 81 public void writeString(String v) { writeUTF16(v); } 82 public native void writeByteArray(byte[] v); 83 84 public void writeRef(Ref ref) { 85 tracker.inc(ref); 86 writeInt32(ref.refnum); 87 } 88 89 public Ref readRef() { 90 int refnum = readInt32(); 91 return tracker.get(refnum); 92 } 93 94 static native void initSeq(); 95 96 // Informs the Go ref tracker that Java is done with this ref. 97 static native void destroyRef(int refnum); 98 99 // createRef creates a Ref to a Java object. 100 public static Ref createRef(Seq.Object o) { 101 return tracker.createRef(o); 102 } 103 104 // sends a function invocation request to Go. 105 // 106 // Blocks until the function completes. 107 // If the request is for a method, the first element in src is 108 // a Ref to the receiver. 109 public static native void send(String descriptor, int code, Seq src, Seq dst); 110 111 // recv returns the next request from Go for a Java call. 112 static native void recv(Seq in, Receive params); 113 114 // recvRes sends the result of a Java call back to Go. 115 static native void recvRes(int handle, Seq out); 116 117 static final class Receive { 118 int refnum; 119 int code; 120 int handle; 121 } 122 123 protected void finalize() throws Throwable { 124 super.finalize(); 125 free(); 126 } 127 private native void free(); 128 129 private static final ExecutorService receivePool = Executors.newCachedThreadPool(); 130 131 // receive listens for callback requests from Go, invokes them on a thread 132 // pool and sends the responses. 133 public static void receive() { 134 Seq.Receive params = new Seq.Receive(); 135 while (true) { 136 final Seq in = new Seq(); 137 Seq.recv(in, params); 138 139 final int code = params.code; 140 final int handle = params.handle; 141 final int refnum = params.refnum; 142 143 if (code == -1) { 144 // Special signal from seq.FinalizeRef. 145 tracker.dec(refnum); 146 Seq out = new Seq(); 147 Seq.recvRes(handle, out); 148 continue; 149 } 150 151 receivePool.execute(new Runnable() { 152 public void run() { 153 Ref r = tracker.get(refnum); 154 Seq out = new Seq(); 155 r.obj.call(code, in, out); 156 Seq.recvRes(handle, out); 157 } 158 }); 159 } 160 } 161 162 // An Object is a Java object that matches a Go object. 163 // The implementation of the object may be in either Java or Go, 164 // with a proxy instance in the other language passing calls 165 // through to the other language. 166 // 167 // Don't implement an Object directly. Instead, look for the 168 // generated abstract Stub. 169 public interface Object { 170 public Ref ref(); 171 public void call(int code, Seq in, Seq out); 172 } 173 174 // A Ref is an object tagged with an integer for passing back and 175 // forth across the language boundary. 176 // 177 // A Ref may represent either an instance of a Java Object subclass, 178 // or an instance of a Go object. The explicit allocation of a Ref 179 // is used to pin Go object instances when they are passed to Java. 180 // The Go Seq library maintains a reference to the instance in a map 181 // keyed by the Ref number. When the JVM calls finalize, we ask Go 182 // to clear the entry in the map. 183 public static final class Ref { 184 // refnum < 0: Go object tracked by Java 185 // refnum > 0: Java object tracked by Go 186 int refnum; 187 188 int refcnt; // for Java obj: track how many times sent to Go. 189 190 public Seq.Object obj; // for Java obj: pointers to the Java obj. 191 192 private Ref(int refnum, Seq.Object o) { 193 this.refnum = refnum; 194 this.refcnt = 0; 195 this.obj = o; 196 } 197 198 @Override 199 protected void finalize() throws Throwable { 200 if (refnum < 0) { 201 // Go object: signal Go to decrement the reference count. 202 Seq.destroyRef(refnum); 203 } 204 super.finalize(); 205 } 206 } 207 208 static final RefTracker tracker = new RefTracker(); 209 210 static final class RefTracker { 211 private static final int REF_OFFSET = 42; 212 213 // use single Ref for null Seq.Object 214 private static final Ref nullRef = new Ref(REF_OFFSET - 1, null); 215 216 // Next Java object reference number. 217 // 218 // Reference numbers are positive for Java objects, 219 // and start, arbitrarily at a different offset to Go 220 // to make debugging by reading Seq hex a little easier. 221 private int next = REF_OFFSET; // next Java object ref 222 223 // Java objects that have been passed to Go. refnum -> Ref 224 // The Ref obj field is non-null. 225 // This map pins Java objects so they don't get GCed while the 226 // only reference to them is held by Go code. 227 private RefMap javaObjs = new RefMap(); 228 229 // inc increments the reference count of a Java object when it 230 // is sent to Go. 231 synchronized void inc(Ref ref) { 232 int refnum = ref.refnum; 233 if (refnum <= 0) { 234 // We don't keep track of the Go object. 235 return; 236 } 237 if (refnum == nullRef.refnum) { 238 return; 239 } 240 // Count how many times this ref's Java object is passed to Go. 241 if (ref.refcnt == Integer.MAX_VALUE) { 242 throw new RuntimeException("refnum " + refnum + " overflow"); 243 } 244 ref.refcnt++; 245 Ref obj = javaObjs.get(refnum); 246 if (obj == null) { 247 javaObjs.put(refnum, ref); 248 } 249 } 250 251 // dec decrements the reference count of a Java object when 252 // Go signals a corresponding proxy object is finalized. 253 // If the count reaches zero, the Java object is removed 254 // from the javaObjs map. 255 synchronized void dec(int refnum) { 256 if (refnum <= 0) { 257 // We don't keep track of the Go object. 258 // This must not happen. 259 log.severe("dec request for Go object "+ refnum); 260 return; 261 } 262 if (refnum == nullRef.refnum) { 263 return; 264 } 265 // Java objects are removed on request of Go. 266 Ref obj = javaObjs.get(refnum); 267 if (obj == null) { 268 throw new RuntimeException("referenced Java object is not found: refnum="+refnum); 269 } 270 obj.refcnt--; 271 if (obj.refcnt <= 0) { 272 javaObjs.remove(refnum); 273 } 274 } 275 276 synchronized Ref createRef(Seq.Object o) { 277 if (o == null) { 278 return nullRef; 279 } 280 if (next == Integer.MAX_VALUE) { 281 throw new RuntimeException("createRef overflow for " + o); 282 } 283 int refnum = next++; 284 Ref ref = new Ref(refnum, o); 285 javaObjs.put(refnum, ref); 286 return ref; 287 } 288 289 // get returns an existing Ref to either a Java or Go object. 290 // It may be the first time we have seen the Go object. 291 // 292 // TODO(crawshaw): We could cut down allocations for frequently 293 // sent Go objects by maintaining a map to weak references. This 294 // however, would require allocating two objects per reference 295 // instead of one. It also introduces weak references, the bane 296 // of any Java debugging session. 297 // 298 // When we have real code, examine the tradeoffs. 299 synchronized Ref get(int refnum) { 300 if (refnum > 0) { 301 if (refnum == nullRef.refnum) { 302 return nullRef; 303 } 304 Ref ref = javaObjs.get(refnum); 305 if (ref == null) { 306 throw new RuntimeException("unknown java Ref: "+refnum); 307 } 308 return ref; 309 } else { 310 // Go object. 311 return new Ref(refnum, null); 312 } 313 } 314 } 315 316 // RefMap is a mapping of integers to Ref objects. 317 // 318 // The integers can be sparse. In Go this would be a map[int]*Ref. 319 private static final class RefMap { 320 private int next = 0; 321 private int live = 0; 322 private int[] keys = new int[16]; 323 private Ref[] objs = new Ref[16]; 324 325 RefMap() {} 326 327 Ref get(int key) { 328 int i = Arrays.binarySearch(keys, 0, next, key); 329 if (i >= 0) { 330 return objs[i]; 331 } 332 return null; 333 } 334 335 void remove(int key) { 336 int i = Arrays.binarySearch(keys, 0, next, key); 337 if (i >= 0) { 338 if (objs[i] != null) { 339 objs[i] = null; 340 live--; 341 } 342 } 343 } 344 345 void put(int key, Ref obj) { 346 if (obj == null) { 347 throw new RuntimeException("put a null ref (with key "+key+")"); 348 } 349 int i = Arrays.binarySearch(keys, 0, next, key); 350 if (i >= 0) { 351 if (objs[i] == null) { 352 objs[i] = obj; 353 } 354 if (objs[i] != obj) { 355 throw new RuntimeException("replacing an existing ref (with key "+key+")"); 356 } 357 return; 358 } 359 if (next >= keys.length) { 360 grow(); 361 i = Arrays.binarySearch(keys, 0, next, key); 362 } 363 i = ~i; 364 if (i < next) { 365 // Insert, shift everything afterwards down. 366 System.arraycopy(keys, i, keys, i+1, next-i); 367 System.arraycopy(objs, i, objs, i+1, next-i); 368 } 369 keys[i] = key; 370 objs[i] = obj; 371 live++; 372 next++; 373 } 374 375 private void grow() { 376 // Compact and (if necessary) grow backing store. 377 int[] newKeys; 378 Ref[] newObjs; 379 int len = 2*roundPow2(live); 380 if (len > keys.length) { 381 newKeys = new int[keys.length*2]; 382 newObjs = new Ref[objs.length*2]; 383 } else { 384 newKeys = keys; 385 newObjs = objs; 386 } 387 388 int j = 0; 389 for (int i = 0; i < keys.length; i++) { 390 if (objs[i] != null) { 391 newKeys[j] = keys[i]; 392 newObjs[j] = objs[i]; 393 j++; 394 } 395 } 396 for (int i = j; i < newKeys.length; i++) { 397 newKeys[i] = 0; 398 newObjs[i] = null; 399 } 400 401 keys = newKeys; 402 objs = newObjs; 403 next = j; 404 405 if (live != next) { 406 throw new RuntimeException("bad state: live="+live+", next="+next); 407 } 408 } 409 410 private static int roundPow2(int x) { 411 int p = 1; 412 while (p < x) { 413 p *= 2; 414 } 415 return p; 416 } 417 } 418 }