Seq.java 8.0 KB

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