| OLD | NEW |
| (Empty) |
| 1 package go; | |
| 2 | |
| 3 import android.util.Log; | |
| 4 import android.util.SparseArray; | |
| 5 import android.util.SparseIntArray; | |
| 6 | |
| 7 import java.util.concurrent.ExecutorService; | |
| 8 import java.util.concurrent.Executors; | |
| 9 | |
| 10 // Seq is a sequence of machine-dependent encoded values. | |
| 11 // Used by automatically generated language bindings to talk to Go. | |
| 12 public class Seq { | |
| 13 @SuppressWarnings("UnusedDeclaration") | |
| 14 private long memptr; // holds C-allocated pointer | |
| 15 | |
| 16 public Seq() { | |
| 17 ensure(64); | |
| 18 } | |
| 19 | |
| 20 // Ensure that at least size bytes can be written to the Seq. | |
| 21 // Any existing data in the buffer is preserved. | |
| 22 public native void ensure(int size); | |
| 23 | |
| 24 // Moves the internal buffer offset back to zero. | |
| 25 // Length and contents are maintained. Data can be read after a reset. | |
| 26 public native void resetOffset(); | |
| 27 | |
| 28 public native void log(String label); | |
| 29 | |
| 30 public native byte readInt8(); | |
| 31 public native short readInt16(); | |
| 32 public native int readInt32(); | |
| 33 public native long readInt64(); | |
| 34 public long readInt() { return readInt64(); } | |
| 35 | |
| 36 public native float readFloat32(); | |
| 37 public native double readFloat64(); | |
| 38 public native String readUTF16(); | |
| 39 public native byte[] readByteArray(); | |
| 40 | |
| 41 public native void writeInt8(byte v); | |
| 42 public native void writeInt16(short v); | |
| 43 public native void writeInt32(int v); | |
| 44 public native void writeInt64(long v); | |
| 45 public void writeInt(long v) { writeInt64(v); } | |
| 46 | |
| 47 public native void writeFloat32(float v); | |
| 48 public native void writeFloat64(double v); | |
| 49 public native void writeUTF16(String v); | |
| 50 public native void writeByteArray(byte[] v); | |
| 51 | |
| 52 public void writeRef(Ref ref) { | |
| 53 writeInt32(ref.refnum); | |
| 54 } | |
| 55 | |
| 56 public Ref readRef() { | |
| 57 int refnum = readInt32(); | |
| 58 return tracker.get(refnum); | |
| 59 } | |
| 60 | |
| 61 // Informs the Go ref tracker that Java is done with this ref. | |
| 62 static native void destroyRef(int refnum); | |
| 63 | |
| 64 // createRef creates a Ref to a Java object. | |
| 65 public static Ref createRef(Seq.Object o) { | |
| 66 return tracker.createRef(o); | |
| 67 } | |
| 68 | |
| 69 // sends a function invocation request to Go. | |
| 70 // | |
| 71 // Blocks until the function completes. | |
| 72 // If the request is for a method, the first element in src is | |
| 73 // a Ref to the receiver. | |
| 74 public static native void send(String descriptor, int code, Seq src, Seq
dst); | |
| 75 | |
| 76 // recv returns the next request from Go for a Java call. | |
| 77 static native void recv(Seq in, Receive params); | |
| 78 | |
| 79 // recvRes sends the result of a Java call back to Go. | |
| 80 static native void recvRes(int handle, Seq out); | |
| 81 | |
| 82 static final class Receive { | |
| 83 int refnum; | |
| 84 int code; | |
| 85 int handle; | |
| 86 } | |
| 87 | |
| 88 protected void finalize() throws Throwable { | |
| 89 super.finalize(); | |
| 90 free(); | |
| 91 } | |
| 92 private native void free(); | |
| 93 | |
| 94 private static final ExecutorService receivePool = Executors.newCachedTh
readPool(); | |
| 95 | |
| 96 // receive listens for callback requests from Go, invokes them on a thre
ad | |
| 97 // pool and sends the responses. | |
| 98 public static void receive() { | |
| 99 Seq.Receive params = new Seq.Receive(); | |
| 100 while (true) { | |
| 101 final Seq in = new Seq(); | |
| 102 Seq.recv(in, params); | |
| 103 | |
| 104 final int code = params.code; | |
| 105 final int handle = params.handle; | |
| 106 final int refnum = params.refnum; | |
| 107 | |
| 108 if (code == -1) { | |
| 109 // Special signal from seq.FinalizeRef. | |
| 110 tracker.dec(refnum); | |
| 111 Seq out = new Seq(); | |
| 112 Seq.recvRes(handle, out); | |
| 113 continue; | |
| 114 } | |
| 115 | |
| 116 receivePool.execute(new Runnable() { | |
| 117 public void run() { | |
| 118 Ref r = tracker.get(refnum); | |
| 119 Seq out = new Seq(); | |
| 120 r.obj.call(code, in, out); | |
| 121 Seq.recvRes(handle, out); | |
| 122 } | |
| 123 }); | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 // An Object is a Java object that matches a Go object. | |
| 128 // The implementation of the object may be in either Java or Go, | |
| 129 // with a proxy instance in the other language passing calls | |
| 130 // through to the other language. | |
| 131 // | |
| 132 // Don't implement an Object directly. Instead, look for the | |
| 133 // generated abstract Stub. | |
| 134 public interface Object { | |
| 135 public Ref ref(); | |
| 136 public void call(int code, Seq in, Seq out); | |
| 137 } | |
| 138 | |
| 139 // A Ref is an object tagged with an integer for passing back and | |
| 140 // forth across the language boundary. | |
| 141 // | |
| 142 // A Ref may represent either an instance of a Java Object subclass, | |
| 143 // or an instance of a Go object. The explicit allocation of a Ref | |
| 144 // is used to pin Go object instances when they are passed to Java. | |
| 145 // The Go Seq library maintains a reference to the instance in a map | |
| 146 // keyed by the Ref number. When the JVM calls finalize, we ask Go | |
| 147 // to clear the entry in the map. | |
| 148 public static final class Ref { | |
| 149 // ref < 0: Go object tracked by Java | |
| 150 // ref > 0: Java object tracked by Go | |
| 151 int refnum; | |
| 152 public Seq.Object obj; | |
| 153 | |
| 154 private Ref(int refnum, Seq.Object o) { | |
| 155 this.refnum = refnum; | |
| 156 this.obj = o; | |
| 157 tracker.inc(refnum); | |
| 158 } | |
| 159 | |
| 160 @Override | |
| 161 protected void finalize() throws Throwable { | |
| 162 tracker.dec(refnum); | |
| 163 super.finalize(); | |
| 164 } | |
| 165 } | |
| 166 | |
| 167 static final RefTracker tracker = new RefTracker(); | |
| 168 | |
| 169 static final class RefTracker { | |
| 170 // Next Java object reference number. | |
| 171 // | |
| 172 // Reference numbers are positive for Java objects, | |
| 173 // and start, arbitrarily at a different offset to Go | |
| 174 // to make debugging by reading Seq hex a little easier. | |
| 175 private int next = 42; // next Java object ref | |
| 176 | |
| 177 // TODO(crawshaw): We could cut down allocations for frequently | |
| 178 // sent Go objects by maintaining a map to weak references. This | |
| 179 // however, would require allocating two objects per reference | |
| 180 // instead of one. It also introduces weak references, the bane | |
| 181 // of any Java debugging session. | |
| 182 // | |
| 183 // When we have real code, examine the tradeoffs. | |
| 184 | |
| 185 // Number of active references to a Go object. refnum -> count | |
| 186 private SparseIntArray goObjs = new SparseIntArray(); | |
| 187 | |
| 188 // Java objects that have been passed to Go. refnum -> Ref | |
| 189 // The Ref obj field is non-null. | |
| 190 // This map pins Java objects so they don't get GCed while the | |
| 191 // only reference to them is held by Go code. | |
| 192 private SparseArray<Ref> javaObjs = new SparseArray<Ref>(); | |
| 193 | |
| 194 // inc increments the reference count to a Go object. | |
| 195 synchronized void inc(int refnum) { | |
| 196 if (refnum > 0) { | |
| 197 return; // we don't count java objects | |
| 198 } | |
| 199 int count = goObjs.get(refnum); | |
| 200 if (count == Integer.MAX_VALUE) { | |
| 201 throw new RuntimeException("refnum " + refnum +
" overflow"); | |
| 202 } | |
| 203 goObjs.put(refnum, count+1); | |
| 204 } | |
| 205 | |
| 206 // dec decrements the reference count to a Go object. | |
| 207 // If the count reaches zero, the Go reference tracker is inform
ed. | |
| 208 synchronized void dec(int refnum) { | |
| 209 if (refnum > 0) { | |
| 210 // Java objects are removed on request of Go. | |
| 211 javaObjs.remove(refnum); | |
| 212 return; | |
| 213 } | |
| 214 int count = goObjs.get(refnum); | |
| 215 if (count == 0) { | |
| 216 throw new RuntimeException("refnum " + refnum +
" underflow"); | |
| 217 } | |
| 218 count--; | |
| 219 if (count <= 0) { | |
| 220 goObjs.delete(refnum); | |
| 221 Seq.destroyRef(refnum); | |
| 222 } else { | |
| 223 goObjs.put(refnum, count); | |
| 224 } | |
| 225 } | |
| 226 | |
| 227 synchronized Ref createRef(Seq.Object o) { | |
| 228 // TODO(crawshaw): use single Ref for null. | |
| 229 if (next == Integer.MAX_VALUE) { | |
| 230 throw new RuntimeException("createRef overflow f
or " + o); | |
| 231 } | |
| 232 int refnum = next++; | |
| 233 Ref ref = new Ref(refnum, o); | |
| 234 javaObjs.put(refnum, ref); | |
| 235 return ref; | |
| 236 } | |
| 237 | |
| 238 // get returns an existing Ref to either a Java or Go object. | |
| 239 // It may be the first time we have seen the Go object. | |
| 240 synchronized Ref get(int refnum) { | |
| 241 if (refnum > 0) { | |
| 242 Ref ref = javaObjs.get(refnum); | |
| 243 if (ref == null) { | |
| 244 throw new RuntimeException("unknown java
Ref: "+refnum); | |
| 245 } | |
| 246 return ref; | |
| 247 } | |
| 248 return new Ref(refnum, null); | |
| 249 } | |
| 250 } | |
| 251 } | |
| OLD | NEW |