Seq.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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. package go;
  5. import android.content.Context;
  6. import java.lang.ref.PhantomReference;
  7. import java.lang.ref.Reference;
  8. import java.lang.ref.ReferenceQueue;
  9. import java.util.Arrays;
  10. import java.util.Collection;
  11. import java.util.Collections;
  12. import java.util.IdentityHashMap;
  13. import java.util.HashSet;
  14. import java.util.Set;
  15. import java.util.logging.Logger;
  16. import go.Universe;
  17. // Seq is a sequence of machine-dependent encoded values.
  18. // Used by automatically generated language bindings to talk to Go.
  19. public class Seq {
  20. private static Logger log = Logger.getLogger("GoSeq");
  21. // also known to bind/seq/ref.go and bind/objc/seq_darwin.m
  22. private static final int NULL_REFNUM = 41;
  23. // use single Ref for null Object
  24. public static final Ref nullRef = new Ref(NULL_REFNUM, null);
  25. // The singleton GoRefQueue
  26. private static final GoRefQueue goRefQueue = new GoRefQueue();
  27. static {
  28. System.loadLibrary("gojni");
  29. init();
  30. Universe.touch();
  31. }
  32. // setContext sets the context in the go-library to be used in RunOnJvm.
  33. public static void setContext(Context context) {
  34. setContext((java.lang.Object)context);
  35. }
  36. private static native void init();
  37. // Empty method to run class initializer
  38. public static void touch() {}
  39. private Seq() {
  40. }
  41. // ctx is an android.context.Context.
  42. static native void setContext(java.lang.Object ctx);
  43. public static void incRefnum(int refnum) {
  44. tracker.incRefnum(refnum);
  45. }
  46. // incRef increments the reference count of Java objects.
  47. // For proxies for Go objects, it calls into the Proxy method
  48. // incRefnum() to make sure the Go reference count is positive
  49. // even if the Proxy is garbage collected and its Ref is finalized.
  50. public static int incRef(Object o) {
  51. return tracker.inc(o);
  52. }
  53. public static int incGoObjectRef(GoObject o) {
  54. return o.incRefnum();
  55. }
  56. // trackGoRef tracks a Go reference and decrements its refcount
  57. // when the given GoObject wrapper is garbage collected.
  58. //
  59. // TODO(crawshaw): We could cut down allocations for frequently
  60. // sent Go objects by maintaining a map to weak references. This
  61. // however, would require allocating two objects per reference
  62. // instead of one. It also introduces weak references, the bane
  63. // of any Java debugging session.
  64. //
  65. // When we have real code, examine the tradeoffs.
  66. public static void trackGoRef(int refnum, GoObject obj) {
  67. if (refnum > 0) {
  68. throw new RuntimeException("trackGoRef called with Java refnum " + refnum);
  69. }
  70. goRefQueue.track(refnum, obj);
  71. }
  72. public static Ref getRef(int refnum) {
  73. return tracker.get(refnum);
  74. }
  75. // Increment the Go reference count before sending over a refnum.
  76. // The ref parameter is only used to make sure the referenced
  77. // object is not garbage collected before Go increments the
  78. // count. It's the equivalent of Go's runtime.KeepAlive.
  79. public static native void incGoRef(int refnum, GoObject ref);
  80. // Informs the Go ref tracker that Java is done with this refnum.
  81. static native void destroyRef(int refnum);
  82. // decRef is called from seq.FinalizeRef
  83. static void decRef(int refnum) {
  84. tracker.dec(refnum);
  85. }
  86. // A GoObject is a Java class implemented in Go. When a GoObject
  87. // is passed to Go, it is wrapped in a Go proxy, to make it behave
  88. // the same as passing a regular Java class.
  89. public interface GoObject {
  90. // Increment refcount and return the refnum of the proxy.
  91. //
  92. // The Go reference count need to be bumped while the
  93. // refnum is passed to Go, to avoid finalizing and
  94. // invalidating it before being translated on the Go side.
  95. int incRefnum();
  96. }
  97. // A Proxy is a Java object that proxies a Go object. Proxies, unlike
  98. // GoObjects, are unwrapped to their Go counterpart when deserialized
  99. // in Go.
  100. public interface Proxy extends GoObject {}
  101. // A Ref represents an instance of a Java object passed back and forth
  102. // across the language boundary.
  103. public static final class Ref {
  104. public final int refnum;
  105. private int refcnt; // Track how many times sent to Go.
  106. public final Object obj; // The referenced Java obj.
  107. Ref(int refnum, Object o) {
  108. if (refnum < 0) {
  109. throw new RuntimeException("Ref instantiated with a Go refnum " + refnum);
  110. }
  111. this.refnum = refnum;
  112. this.refcnt = 0;
  113. this.obj = o;
  114. }
  115. void inc() {
  116. // Count how many times this ref's Java object is passed to Go.
  117. if (refcnt == Integer.MAX_VALUE) {
  118. throw new RuntimeException("refnum " + refnum + " overflow");
  119. }
  120. refcnt++;
  121. }
  122. }
  123. static final RefTracker tracker = new RefTracker();
  124. static final class RefTracker {
  125. private static final int REF_OFFSET = 42;
  126. // Next Java object reference number.
  127. //
  128. // Reference numbers are positive for Java objects,
  129. // and start, arbitrarily at a different offset to Go
  130. // to make debugging by reading Seq hex a little easier.
  131. private int next = REF_OFFSET; // next Java object ref
  132. // Java objects that have been passed to Go. refnum -> Ref
  133. // The Ref obj field is non-null.
  134. // This map pins Java objects so they don't get GCed while the
  135. // only reference to them is held by Go code.
  136. private final RefMap javaObjs = new RefMap();
  137. // Java objects to refnum
  138. private final IdentityHashMap<Object, Integer> javaRefs = new IdentityHashMap<>();
  139. // inc increments the reference count of a Java object when it
  140. // is sent to Go. inc returns the refnum for the object.
  141. synchronized int inc(Object o) {
  142. if (o == null) {
  143. return NULL_REFNUM;
  144. }
  145. if (o instanceof Proxy) {
  146. return ((Proxy)o).incRefnum();
  147. }
  148. Integer refnumObj = javaRefs.get(o);
  149. if (refnumObj == null) {
  150. if (next == Integer.MAX_VALUE) {
  151. throw new RuntimeException("createRef overflow for " + o);
  152. }
  153. refnumObj = next++;
  154. javaRefs.put(o, refnumObj);
  155. }
  156. int refnum = refnumObj;
  157. Ref ref = javaObjs.get(refnum);
  158. if (ref == null) {
  159. ref = new Ref(refnum, o);
  160. javaObjs.put(refnum, ref);
  161. }
  162. ref.inc();
  163. return refnum;
  164. }
  165. synchronized void incRefnum(int refnum) {
  166. Ref ref = javaObjs.get(refnum);
  167. if (ref == null) {
  168. throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
  169. }
  170. ref.inc();
  171. }
  172. // dec decrements the reference count of a Java object when
  173. // Go signals a corresponding proxy object is finalized.
  174. // If the count reaches zero, the Java object is removed
  175. // from the javaObjs map.
  176. synchronized void dec(int refnum) {
  177. if (refnum <= 0) {
  178. // We don't keep track of the Go object.
  179. // This must not happen.
  180. log.severe("dec request for Go object "+ refnum);
  181. return;
  182. }
  183. if (refnum == Seq.nullRef.refnum) {
  184. return;
  185. }
  186. // Java objects are removed on request of Go.
  187. Ref obj = javaObjs.get(refnum);
  188. if (obj == null) {
  189. throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
  190. }
  191. obj.refcnt--;
  192. if (obj.refcnt <= 0) {
  193. javaObjs.remove(refnum);
  194. javaRefs.remove(obj.obj);
  195. }
  196. }
  197. // get returns an existing Ref to a Java object.
  198. synchronized Ref get(int refnum) {
  199. if (refnum < 0) {
  200. throw new RuntimeException("ref called with Go refnum " + refnum);
  201. }
  202. if (refnum == NULL_REFNUM) {
  203. return nullRef;
  204. }
  205. Ref ref = javaObjs.get(refnum);
  206. if (ref == null) {
  207. throw new RuntimeException("unknown java Ref: "+refnum);
  208. }
  209. return ref;
  210. }
  211. }
  212. // GoRefQueue is a queue of GoRefs that are no longer live. An internal thread
  213. // processes the queue and decrement the reference count on the Go side.
  214. static class GoRefQueue extends ReferenceQueue<GoObject> {
  215. // The set of tracked GoRefs. If we don't hold on to the GoRef instances, the Java GC
  216. // will not add them to the queue when their referents are reclaimed.
  217. private final Collection<GoRef> refs = Collections.synchronizedCollection(new HashSet<GoRef>());
  218. void track(int refnum, GoObject obj) {
  219. refs.add(new GoRef(refnum, obj, this));
  220. }
  221. GoRefQueue() {
  222. Thread daemon = new Thread(new Runnable() {
  223. @Override public void run() {
  224. while (true) {
  225. try {
  226. GoRef ref = (GoRef)remove();
  227. refs.remove(ref);
  228. destroyRef(ref.refnum);
  229. ref.clear();
  230. } catch (InterruptedException e) {
  231. // Ignore
  232. }
  233. }
  234. }
  235. });
  236. daemon.setDaemon(true);
  237. daemon.setName("GoRefQueue Finalizer Thread");
  238. daemon.start();
  239. }
  240. }
  241. // A GoRef is a PhantomReference to a Java proxy for a Go object.
  242. // GoRefs are enqueued to the singleton GoRefQueue when no longer live,
  243. // so the corresponding reference count can be decremented.
  244. static class GoRef extends PhantomReference<GoObject> {
  245. final int refnum;
  246. GoRef(int refnum, GoObject obj, GoRefQueue q) {
  247. super(obj, q);
  248. if (refnum > 0) {
  249. throw new RuntimeException("GoRef instantiated with a Java refnum " + refnum);
  250. }
  251. this.refnum = refnum;
  252. }
  253. }
  254. // RefMap is a mapping of integers to Ref objects.
  255. //
  256. // The integers can be sparse. In Go this would be a map[int]*Ref.
  257. static final class RefMap {
  258. private int next = 0;
  259. private int live = 0;
  260. private int[] keys = new int[16];
  261. private Ref[] objs = new Ref[16];
  262. RefMap() {}
  263. Ref get(int key) {
  264. int i = Arrays.binarySearch(keys, 0, next, key);
  265. if (i >= 0) {
  266. return objs[i];
  267. }
  268. return null;
  269. }
  270. void remove(int key) {
  271. int i = Arrays.binarySearch(keys, 0, next, key);
  272. if (i >= 0) {
  273. if (objs[i] != null) {
  274. objs[i] = null;
  275. live--;
  276. }
  277. }
  278. }
  279. void put(int key, Ref obj) {
  280. if (obj == null) {
  281. throw new RuntimeException("put a null ref (with key "+key+")");
  282. }
  283. int i = Arrays.binarySearch(keys, 0, next, key);
  284. if (i >= 0) {
  285. if (objs[i] == null) {
  286. objs[i] = obj;
  287. live++;
  288. }
  289. if (objs[i] != obj) {
  290. throw new RuntimeException("replacing an existing ref (with key "+key+")");
  291. }
  292. return;
  293. }
  294. if (next >= keys.length) {
  295. grow();
  296. i = Arrays.binarySearch(keys, 0, next, key);
  297. }
  298. i = ~i;
  299. if (i < next) {
  300. // Insert, shift everything afterwards down.
  301. System.arraycopy(keys, i, keys, i+1, next-i);
  302. System.arraycopy(objs, i, objs, i+1, next-i);
  303. }
  304. keys[i] = key;
  305. objs[i] = obj;
  306. live++;
  307. next++;
  308. }
  309. private void grow() {
  310. // Compact and (if necessary) grow backing store.
  311. int[] newKeys;
  312. Ref[] newObjs;
  313. int len = 2*roundPow2(live);
  314. if (len > keys.length) {
  315. newKeys = new int[keys.length*2];
  316. newObjs = new Ref[objs.length*2];
  317. } else {
  318. newKeys = keys;
  319. newObjs = objs;
  320. }
  321. int j = 0;
  322. for (int i = 0; i < keys.length; i++) {
  323. if (objs[i] != null) {
  324. newKeys[j] = keys[i];
  325. newObjs[j] = objs[i];
  326. j++;
  327. }
  328. }
  329. for (int i = j; i < newKeys.length; i++) {
  330. newKeys[i] = 0;
  331. newObjs[i] = null;
  332. }
  333. keys = newKeys;
  334. objs = newObjs;
  335. next = j;
  336. if (live != next) {
  337. throw new RuntimeException("bad state: live="+live+", next="+next);
  338. }
  339. }
  340. private static int roundPow2(int x) {
  341. int p = 1;
  342. while (p < x) {
  343. p *= 2;
  344. }
  345. return p;
  346. }
  347. }
  348. }