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  }