github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/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.app.Application; 8 import android.content.Context; 9 import android.util.Log; 10 import android.util.SparseArray; 11 import android.util.SparseIntArray; 12 13 import java.util.concurrent.ExecutorService; 14 import java.util.concurrent.Executors; 15 16 // Seq is a sequence of machine-dependent encoded values. 17 // Used by automatically generated language bindings to talk to Go. 18 public class Seq { 19 static { 20 // Look for the shim class auto-generated by gomobile bind. 21 // Its only purpose is to call System.loadLibrary. 22 try { 23 Class.forName("go.LoadJNI"); 24 } catch (ClassNotFoundException e) { 25 // Ignore, assume the user will load JNI for it. 26 Log.w("GoSeq", "LoadJNI class not found"); 27 } 28 29 try { 30 // TODO(hyangah): check proguard rule. 31 Application appl = (Application)Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null); 32 Context ctx = appl.getApplicationContext(); 33 setContext(ctx); 34 35 } catch (Exception e) { 36 Log.w("GoSeq", "Global context not found:" + e); 37 } 38 39 initSeq(); 40 new Thread("GoSeq") { 41 public void run() { Seq.receive(); } 42 }.start(); 43 } 44 45 @SuppressWarnings("UnusedDeclaration") 46 private long memptr; // holds C-allocated pointer 47 48 public Seq() { 49 ensure(64); 50 } 51 52 static native void setContext(Context ctx); 53 54 // Ensure that at least size bytes can be written to the Seq. 55 // Any existing data in the buffer is preserved. 56 public native void ensure(int size); 57 58 // Moves the internal buffer offset back to zero. 59 // Length and contents are maintained. Data can be read after a reset. 60 public native void resetOffset(); 61 62 public native void log(String label); 63 64 public native boolean readBool(); 65 public native byte readInt8(); 66 public native short readInt16(); 67 public native int readInt32(); 68 public native long readInt64(); 69 public long readInt() { return readInt64(); } 70 71 public native float readFloat32(); 72 public native double readFloat64(); 73 public native String readUTF16(); 74 public String readString() { return readUTF16(); } 75 public native byte[] readByteArray(); 76 77 public native void writeBool(boolean v); 78 public native void writeInt8(byte v); 79 public native void writeInt16(short v); 80 public native void writeInt32(int v); 81 public native void writeInt64(long v); 82 public void writeInt(long v) { writeInt64(v); } 83 84 public native void writeFloat32(float v); 85 public native void writeFloat64(double v); 86 public native void writeUTF16(String v); 87 public void writeString(String v) { writeUTF16(v); } 88 public native void writeByteArray(byte[] v); 89 90 public void writeRef(Ref ref) { 91 tracker.inc(ref); 92 writeInt32(ref.refnum); 93 } 94 95 public Ref readRef() { 96 int refnum = readInt32(); 97 return tracker.get(refnum); 98 } 99 100 static native void initSeq(); 101 102 // Informs the Go ref tracker that Java is done with this ref. 103 static native void destroyRef(int refnum); 104 105 // createRef creates a Ref to a Java object. 106 public static Ref createRef(Seq.Object o) { 107 return tracker.createRef(o); 108 } 109 110 // sends a function invocation request to Go. 111 // 112 // Blocks until the function completes. 113 // If the request is for a method, the first element in src is 114 // a Ref to the receiver. 115 public static native void send(String descriptor, int code, Seq src, Seq dst); 116 117 // recv returns the next request from Go for a Java call. 118 static native void recv(Seq in, Receive params); 119 120 // recvRes sends the result of a Java call back to Go. 121 static native void recvRes(int handle, Seq out); 122 123 static final class Receive { 124 int refnum; 125 int code; 126 int handle; 127 } 128 129 protected void finalize() throws Throwable { 130 super.finalize(); 131 free(); 132 } 133 private native void free(); 134 135 private static final ExecutorService receivePool = Executors.newCachedThreadPool(); 136 137 // receive listens for callback requests from Go, invokes them on a thread 138 // pool and sends the responses. 139 public static void receive() { 140 Seq.Receive params = new Seq.Receive(); 141 while (true) { 142 final Seq in = new Seq(); 143 Seq.recv(in, params); 144 145 final int code = params.code; 146 final int handle = params.handle; 147 final int refnum = params.refnum; 148 149 if (code == -1) { 150 // Special signal from seq.FinalizeRef. 151 tracker.dec(refnum); 152 Seq out = new Seq(); 153 Seq.recvRes(handle, out); 154 continue; 155 } 156 157 receivePool.execute(new Runnable() { 158 public void run() { 159 Ref r = tracker.get(refnum); 160 Seq out = new Seq(); 161 r.obj.call(code, in, out); 162 Seq.recvRes(handle, out); 163 } 164 }); 165 } 166 } 167 168 // An Object is a Java object that matches a Go object. 169 // The implementation of the object may be in either Java or Go, 170 // with a proxy instance in the other language passing calls 171 // through to the other language. 172 // 173 // Don't implement an Object directly. Instead, look for the 174 // generated abstract Stub. 175 public interface Object { 176 public Ref ref(); 177 public void call(int code, Seq in, Seq out); 178 } 179 180 // A Ref is an object tagged with an integer for passing back and 181 // forth across the language boundary. 182 // 183 // A Ref may represent either an instance of a Java Object subclass, 184 // or an instance of a Go object. The explicit allocation of a Ref 185 // is used to pin Go object instances when they are passed to Java. 186 // The Go Seq library maintains a reference to the instance in a map 187 // keyed by the Ref number. When the JVM calls finalize, we ask Go 188 // to clear the entry in the map. 189 public static final class Ref { 190 // refnum < 0: Go object tracked by Java 191 // refnum > 0: Java object tracked by Go 192 int refnum; 193 194 int refcnt; // for Java obj: track how many times sent to Go. 195 196 public Seq.Object obj; // for Java obj: pointers to the Java obj. 197 198 private Ref(int refnum, Seq.Object o) { 199 this.refnum = refnum; 200 this.refcnt = 0; 201 this.obj = o; 202 } 203 204 @Override 205 protected void finalize() throws Throwable { 206 if (refnum < 0) { 207 // Go object: signal Go to decrement the reference count. 208 Seq.destroyRef(refnum); 209 } 210 super.finalize(); 211 } 212 } 213 214 static final RefTracker tracker = new RefTracker(); 215 216 static final class RefTracker { 217 // Next Java object reference number. 218 // 219 // Reference numbers are positive for Java objects, 220 // and start, arbitrarily at a different offset to Go 221 // to make debugging by reading Seq hex a little easier. 222 private int next = 42; // next Java object ref 223 224 // Java objects that have been passed to Go. refnum -> Ref 225 // The Ref obj field is non-null. 226 // This map pins Java objects so they don't get GCed while the 227 // only reference to them is held by Go code. 228 private SparseArray<Ref> javaObjs = new SparseArray<Ref>(); 229 230 // inc increments the reference count of a Java object when it 231 // is sent to Go. 232 synchronized void inc(Ref ref) { 233 int refnum = ref.refnum; 234 if (refnum <= 0) { 235 // We don't keep track of the Go object. 236 return; 237 } 238 // Count Java objects passed to Go. 239 if (ref.refcnt == Integer.MAX_VALUE) { 240 throw new RuntimeException("refnum " + refnum + " overflow"); 241 } 242 ref.refcnt++; 243 Ref obj = javaObjs.get(refnum); 244 if (obj == null) { 245 javaObjs.put(refnum, ref); 246 } 247 } 248 249 // dec decrements the reference count of a Java object when 250 // Go signals a corresponding proxy object is finalized. 251 // If the count reaches zero, the Java object is removed 252 // from the javaObjs map. 253 synchronized void dec(int refnum) { 254 if (refnum <= 0) { 255 // We don't keep track of the Go object. 256 // This must not happen. 257 Log.wtf("GoSeq", "dec request for Go object "+ refnum); 258 return; 259 } 260 // Java objects are removed on request of Go. 261 Ref obj = javaObjs.get(refnum); 262 if (obj == null) { 263 throw new RuntimeException("referenced Java object is not found: refnum="+refnum); 264 } 265 obj.refcnt--; 266 if (obj.refcnt <= 0) { 267 javaObjs.remove(refnum); 268 } 269 } 270 271 synchronized Ref createRef(Seq.Object o) { 272 // TODO(crawshaw): use single Ref for null. 273 if (next == Integer.MAX_VALUE) { 274 throw new RuntimeException("createRef overflow for " + o); 275 } 276 int refnum = next++; 277 Ref ref = new Ref(refnum, o); 278 javaObjs.put(refnum, ref); 279 return ref; 280 } 281 282 // get returns an existing Ref to either a Java or Go object. 283 // It may be the first time we have seen the Go object. 284 // 285 // TODO(crawshaw): We could cut down allocations for frequently 286 // sent Go objects by maintaining a map to weak references. This 287 // however, would require allocating two objects per reference 288 // instead of one. It also introduces weak references, the bane 289 // of any Java debugging session. 290 // 291 // When we have real code, examine the tradeoffs. 292 synchronized Ref get(int refnum) { 293 if (refnum > 0) { 294 Ref ref = javaObjs.get(refnum); 295 if (ref == null) { 296 throw new RuntimeException("unknown java Ref: "+refnum); 297 } 298 return ref; 299 } else { 300 // Go object. 301 return new Ref(refnum, null); 302 } 303 } 304 } 305 }