2
0
mirror of synced 2025-02-23 14:58:12 +00:00
mobile/bind/java/Seq.java

403 lines
11 KiB
Java
Raw Normal View History

// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package go;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger;
import go.Universe;
// Seq is a sequence of machine-dependent encoded values.
// Used by automatically generated language bindings to talk to Go.
public class Seq {
private static Logger log = Logger.getLogger("GoSeq");
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
// also known to bind/seq/ref.go and bind/objc/seq_darwin.m
private static final int NULL_REFNUM = 41;
// use single Ref for null Object
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
public static final Ref nullRef = new Ref(NULL_REFNUM, null);
// The singleton GoRefQueue
private static final GoRefQueue goRefQueue = new GoRefQueue();
static {
// Look for the shim class auto-generated by gomobile bind.
// Its only purpose is to call System.loadLibrary.
try {
Class loadJNI = Class.forName("go.LoadJNI");
setContext(loadJNI.getDeclaredField("ctx").get(null));
} catch (ClassNotFoundException e) {
// Ignore, assume the user will load JNI for it.
log.warning("LoadJNI class not found");
} catch (NoSuchFieldException e) {
log.severe("LoadJNI class missing field: " + e);
} catch (IllegalAccessException e) {
log.severe("LoadJNI class bad field: " + e);
}
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
init();
Universe.touch();
}
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
private static native void init();
// Empty method to run class initializer
public static void touch() {}
private Seq() {
}
// ctx is an android.context.Context.
static native void setContext(java.lang.Object ctx);
public static void incRefnum(int refnum) {
tracker.incRefnum(refnum);
}
// incRef increments the reference count of Java objects.
// For proxies for Go objects, it calls into the Proxy method
// incRefnum() to make sure the Go reference count is positive
// even if the Proxy is garbage collected and its Ref is finalized.
public static int incRef(Object o) {
return tracker.inc(o);
}
public static int incGoObjectRef(GoObject o) {
return o.incRefnum();
}
// trackGoRef tracks a Go reference and decrements its refcount
// when the given GoObject wrapper is garbage collected.
//
// TODO(crawshaw): We could cut down allocations for frequently
// sent Go objects by maintaining a map to weak references. This
// however, would require allocating two objects per reference
// instead of one. It also introduces weak references, the bane
// of any Java debugging session.
//
// When we have real code, examine the tradeoffs.
public static void trackGoRef(int refnum, GoObject obj) {
if (refnum > 0) {
throw new RuntimeException("trackGoRef called with Java refnum " + refnum);
}
goRefQueue.track(refnum, obj);
}
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
public static Ref getRef(int refnum) {
return tracker.get(refnum);
}
// Increment the Go reference count before sending over a refnum.
public static native void incGoRef(int refnum);
// Informs the Go ref tracker that Java is done with this refnum.
static native void destroyRef(int refnum);
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
// decRef is called from seq.FinalizeRef
static void decRef(int refnum) {
tracker.dec(refnum);
}
// A GoObject is a Java class implemented in Go. When a GoObject
// is passed to Go, it is wrapped in a Go proxy, to make it behave
// the same as passing a regular Java class.
public interface GoObject {
// Increment refcount and return the refnum of the proxy.
//
// The Go reference count need to be bumped while the
// refnum is passed to Go, to avoid finalizing and
// invalidating it before being translated on the Go side.
int incRefnum();
}
// A Proxy is a Java object that proxies a Go object. Proxies, unlike
// GoObjects, are unwrapped to their Go counterpart when deserialized
// in Go.
bind: remove error wrappers to preserve error instance identity CL 24800 changed the error representation from strings to objects. However, since native errors types are not immediately compatible across languages, wrapper types were introduced to bridge the gap. This CL remove those wrappers and instead special case the error proxy types to conform to their language error protocol. Specifically: - The ObjC proxy for Go errors now extends NSError and calls initWithDomain to store the error message. - The Go proxy for ObjC NSError return the localizedDescription property for calls to Error. - The Java proxy for Go errors ow extends Exception and overrides getMessage() to return the error message. - The Go proxy for Java Exceptions returns getMessage whenever Error is called. The end result is that error values behave more like normal objects across the language boundary. In particular, instance identity is now preserved: an error passed across the boundary and back will result in the same instance. There are two semantic changes that followed this change: - The domain for wrapped Go errors is now always "go". The domain wasn't useful before this CL: the domains were set to the package name of function or method where the error happened to cross the language boundary. - If a Go method that returns an error is implemented in ObjC, the implementation must now both return NO _and_ set the error result for the calling Go code to receive a non-nil error. Before this CL, because errors were always wrapped, a nil ObjC could be represented with a non-nil wrapper. Change-Id: Idb415b6b13ecf79ccceb60f675059942bfc48fec Reviewed-on: https://go-review.googlesource.com/29298 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2016-09-19 12:44:13 +02:00
public interface Proxy extends GoObject {}
// A Ref represents an instance of a Java object passed back and forth
// across the language boundary.
public static final class Ref {
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
public final int refnum;
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
private int refcnt; // Track how many times sent to Go.
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
public final Object obj; // The referenced Java obj.
Ref(int refnum, Object o) {
if (refnum < 0) {
throw new RuntimeException("Ref instantiated with a Go refnum " + refnum);
}
this.refnum = refnum;
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
this.refcnt = 0;
this.obj = o;
}
void inc() {
// Count how many times this ref's Java object is passed to Go.
if (refcnt == Integer.MAX_VALUE) {
throw new RuntimeException("refnum " + refnum + " overflow");
}
refcnt++;
}
}
static final RefTracker tracker = new RefTracker();
static final class RefTracker {
private static final int REF_OFFSET = 42;
// Next Java object reference number.
//
// Reference numbers are positive for Java objects,
// and start, arbitrarily at a different offset to Go
// to make debugging by reading Seq hex a little easier.
private int next = REF_OFFSET; // next Java object ref
// Java objects that have been passed to Go. refnum -> Ref
// The Ref obj field is non-null.
// This map pins Java objects so they don't get GCed while the
// only reference to them is held by Go code.
private final RefMap javaObjs = new RefMap();
// Java objects to refnum
private final IdentityHashMap<Object, Integer> javaRefs = new IdentityHashMap<>();
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
// inc increments the reference count of a Java object when it
// is sent to Go. inc returns the refnum for the object.
synchronized int inc(Object o) {
if (o == null) {
return NULL_REFNUM;
}
if (o instanceof Proxy) {
return ((Proxy)o).incRefnum();
}
Integer refnumObj = javaRefs.get(o);
if (refnumObj == null) {
if (next == Integer.MAX_VALUE) {
throw new RuntimeException("createRef overflow for " + o);
}
refnumObj = next++;
javaRefs.put(o, refnumObj);
}
int refnum = refnumObj;
Ref ref = javaObjs.get(refnum);
if (ref == null) {
ref = new Ref(refnum, o);
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
javaObjs.put(refnum, ref);
}
ref.inc();
return refnum;
}
synchronized void incRefnum(int refnum) {
Ref ref = javaObjs.get(refnum);
if (ref == null) {
throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
}
ref.inc();
}
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
// dec decrements the reference count of a Java object when
// Go signals a corresponding proxy object is finalized.
// If the count reaches zero, the Java object is removed
// from the javaObjs map.
synchronized void dec(int refnum) {
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
if (refnum <= 0) {
// We don't keep track of the Go object.
// This must not happen.
log.severe("dec request for Go object "+ refnum);
return;
}
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
if (refnum == Seq.nullRef.refnum) {
return;
}
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
// Java objects are removed on request of Go.
Ref obj = javaObjs.get(refnum);
if (obj == null) {
throw new RuntimeException("referenced Java object is not found: refnum="+refnum);
}
bind/java: manage Java object lifetime based on reference count. The gobind framework is supposed to use reference counting to keep track of objects (e.g. pointer to a Go struct, interface values) crossing the language boundary. This change fixes two bugs: 1) no reference counting on Java object: Previously, the lifetime of a Java object was manages in the following way. a. The Java object is pinned in an internal map (javaObjs) when it's constructed. b. When Go receives the reference to the Java object, it creates a proxy object and sets a finalizer on it. The finalizer signals Java to unpin the Java object (remove from the javaObjs map). c. The javaObjs map is also used to identify the Java object when Go asks to invoke a method on it later. When the same Java object is sent to Java more than once, and the finalizer (b) runs after the first use, the second use of the Java object can cause the crash described in golang/go#10933. This change fixes the bug by reference counting the Java object. Java side pins the Java object and increments the refcount whenever it sees the object sent to Go (in Seq.writeRef). When the Go proxy object's finalizer runs, the refcount is decremented. When the refcount becomes 0, the object gets unpined. 2) race in Go object lifetime management: Pinning on a Go object has been done when the Go object is sent to Java but the Go object is not in the pinned object map yet. (bind/seq.WriteGoRef). Unpinning the object occurs when Java finds there are no proxy objects on its side. For this, Java maintains a reference count map (goObjs). When the refcount becomes zero, Java notifies Go so the object is unpinned. Here is a race case: a. Java has a proxy object for a Go object. b. Go is preparing for sending the same Go object. seq.WriteGoRef notices the corresponding entry in the pinned object map already, and returns. The remaining work for sending the object continues. c. The proxy object in Java finalizes and triggers deletion of the object from the pinned object map. d. The remaining work for (b) completes and Java creates a new proxy object. When a method is called for the Go object, the Go object is already removed from the object map on Go side and maybe already GC'd. This change fixes it by converting the pinned object map to reference counter map maintained in Go. The counter increments for each seq.WriteGoRef call. The finalizer of the proxy object in Java causes a decrement of the counter. Fixes golang/go#10933. Renables the skipped testJavaRefGC. Change-Id: I0992e002b1050b6183689e5ab821e058adbb420f Reviewed-on: https://go-review.googlesource.com/10638 Reviewed-by: David Crawshaw <crawshaw@golang.org>
2015-06-03 06:12:57 -04:00
obj.refcnt--;
if (obj.refcnt <= 0) {
javaObjs.remove(refnum);
javaRefs.remove(obj.obj);
}
}
// get returns an existing Ref to a Java object.
synchronized Ref get(int refnum) {
if (refnum < 0) {
throw new RuntimeException("ref called with Go refnum " + refnum);
}
mobile/bind: replace seq serialization with direct calls The seq serialization machinery is a historic artifact from when Go mobile code had to run in a separate process. Now that Go code is running in-process, replace the explicit serialization with direct calls and pass arguments on the stack. The benefits are a much smaller bind runtime, much less garbage (and, in Java, fewer objects with finalizers), less argument copying, and faster cross-language calls. The cost is a more complex generator, because some of the work from the bind runtime is moved to generated code. Generated code now handles conversion between Go and Java/ObjC types, multiple return values and memory management of byte slice and string arguments. To overcome the lack of calling C code between Go packages, all bound packages now end up in the same (fake) package, "gomobile_bind", instead of separate packages (go_<pkgname>). To avoid name clashes, the package name is added as a prefix to generated functions and types. Also, don't copy byte arrays passed to Go, saving call time and allowing read([]byte)-style interfaces to foreign callers (#12113). Finally, add support for nil interfaces and struct pointers to objc. This is a large CL, but most of the changes stem from changing testdata. The full benchcmp output on the CL/20095 benchmarks on my Nexus 5 is reproduced below. Note that the savings for the JavaSlice* benchmarks are skewed because byte slices are no longer copied before passing them to Go. benchmark old ns/op new ns/op delta BenchmarkJavaEmpty 26.0 19.0 -26.92% BenchmarkJavaEmptyDirect 23.0 22.0 -4.35% BenchmarkJavaNoargs 7685 2339 -69.56% BenchmarkJavaNoargsDirect 17405 8041 -53.80% BenchmarkJavaOnearg 26887 2366 -91.20% BenchmarkJavaOneargDirect 34266 7910 -76.92% BenchmarkJavaOneret 38325 2245 -94.14% BenchmarkJavaOneretDirect 46265 7708 -83.34% BenchmarkJavaManyargs 41720 2535 -93.92% BenchmarkJavaManyargsDirect 51026 8373 -83.59% BenchmarkJavaRefjava 38139 21260 -44.26% BenchmarkJavaRefjavaDirect 42706 28150 -34.08% BenchmarkJavaRefgo 34403 6843 -80.11% BenchmarkJavaRefgoDirect 40193 16582 -58.74% BenchmarkJavaStringShort 32366 9323 -71.20% BenchmarkJavaStringShortDirect 41973 19118 -54.45% BenchmarkJavaStringLong 127879 94420 -26.16% BenchmarkJavaStringLongDirect 133776 114760 -14.21% BenchmarkJavaStringShortUnicode 32562 9221 -71.68% BenchmarkJavaStringShortUnicodeDirect 41464 19094 -53.95% BenchmarkJavaStringLongUnicode 131015 89401 -31.76% BenchmarkJavaStringLongUnicodeDirect 134130 90786 -32.31% BenchmarkJavaSliceShort 42462 7538 -82.25% BenchmarkJavaSliceShortDirect 52940 17017 -67.86% BenchmarkJavaSliceLong 138391 8466 -93.88% BenchmarkJavaSliceLongDirect 205804 15666 -92.39% BenchmarkGoEmpty 3.00 3.00 +0.00% BenchmarkGoEmptyDirect 3.00 3.00 +0.00% BenchmarkGoNoarg 40342 13716 -66.00% BenchmarkGoNoargDirect 46691 13569 -70.94% BenchmarkGoOnearg 43529 13757 -68.40% BenchmarkGoOneargDirect 44867 14078 -68.62% BenchmarkGoOneret 45456 13559 -70.17% BenchmarkGoOneretDirect 44694 13442 -69.92% BenchmarkGoRefjava 55111 28071 -49.06% BenchmarkGoRefjavaDirect 60883 26872 -55.86% BenchmarkGoRefgo 57038 29223 -48.77% BenchmarkGoRefgoDirect 56153 27812 -50.47% BenchmarkGoManyargs 67967 17398 -74.40% BenchmarkGoManyargsDirect 60617 16998 -71.96% BenchmarkGoStringShort 57538 22600 -60.72% BenchmarkGoStringShortDirect 52627 22704 -56.86% BenchmarkGoStringLong 128485 52530 -59.12% BenchmarkGoStringLongDirect 138377 52079 -62.36% BenchmarkGoStringShortUnicode 57062 22994 -59.70% BenchmarkGoStringShortUnicodeDirect 62563 22938 -63.34% BenchmarkGoStringLongUnicode 139913 55553 -60.29% BenchmarkGoStringLongUnicodeDirect 150863 57791 -61.69% BenchmarkGoSliceShort 59279 20215 -65.90% BenchmarkGoSliceShortDirect 60160 21136 -64.87% BenchmarkGoSliceLong 411225 301870 -26.59% BenchmarkGoSliceLongDirect 399029 298915 -25.09% Fixes golang/go#12619 Fixes golang/go#12113 Fixes golang/go#13033 Change-Id: I2b45e9e98a1248e3c23a5137f775f7364908bec7 Reviewed-on: https://go-review.googlesource.com/19821 Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
2016-02-12 18:50:33 +01:00
if (refnum == NULL_REFNUM) {
return nullRef;
}
Ref ref = javaObjs.get(refnum);
if (ref == null) {
throw new RuntimeException("unknown java Ref: "+refnum);
}
return ref;
}
}
// GoRefQueue is a queue of GoRefs that are no longer live. An internal thread
// processes the queue and decrement the reference count on the Go side.
static class GoRefQueue extends ReferenceQueue<GoObject> {
// The set of tracked GoRefs. If we don't hold on to the GoRef instances, the Java GC
// will not add them to the queue when their referents are reclaimed.
private final Collection<GoRef> refs = Collections.synchronizedCollection(new HashSet<GoRef>());
void track(int refnum, GoObject obj) {
refs.add(new GoRef(refnum, obj, this));
}
GoRefQueue() {
Thread daemon = new Thread(new Runnable() {
@Override public void run() {
while (true) {
try {
GoRef ref = (GoRef)remove();
refs.remove(ref);
destroyRef(ref.refnum);
ref.clear();
} catch (InterruptedException e) {
// Ignore
}
}
}
});
daemon.setDaemon(true);
daemon.setName("GoRefQueue Finalizer Thread");
daemon.start();
}
}
// A GoRef is a PhantomReference to a Java proxy for a Go object.
// GoRefs are enqueued to the singleton GoRefQueue when no longer live,
// so the corresponding reference count can be decremented.
static class GoRef extends PhantomReference<GoObject> {
final int refnum;
GoRef(int refnum, GoObject obj, GoRefQueue q) {
super(obj, q);
if (refnum > 0) {
throw new RuntimeException("GoRef instantiated with a Java refnum " + refnum);
}
this.refnum = refnum;
}
}
// RefMap is a mapping of integers to Ref objects.
//
// The integers can be sparse. In Go this would be a map[int]*Ref.
static final class RefMap {
private int next = 0;
private int live = 0;
private int[] keys = new int[16];
private Ref[] objs = new Ref[16];
RefMap() {}
Ref get(int key) {
int i = Arrays.binarySearch(keys, 0, next, key);
if (i >= 0) {
return objs[i];
}
return null;
}
void remove(int key) {
int i = Arrays.binarySearch(keys, 0, next, key);
if (i >= 0) {
if (objs[i] != null) {
objs[i] = null;
live--;
}
}
}
void put(int key, Ref obj) {
if (obj == null) {
throw new RuntimeException("put a null ref (with key "+key+")");
}
int i = Arrays.binarySearch(keys, 0, next, key);
if (i >= 0) {
if (objs[i] == null) {
objs[i] = obj;
live++;
}
if (objs[i] != obj) {
throw new RuntimeException("replacing an existing ref (with key "+key+")");
}
return;
}
if (next >= keys.length) {
grow();
i = Arrays.binarySearch(keys, 0, next, key);
}
i = ~i;
if (i < next) {
// Insert, shift everything afterwards down.
System.arraycopy(keys, i, keys, i+1, next-i);
System.arraycopy(objs, i, objs, i+1, next-i);
}
keys[i] = key;
objs[i] = obj;
live++;
next++;
}
private void grow() {
// Compact and (if necessary) grow backing store.
int[] newKeys;
Ref[] newObjs;
int len = 2*roundPow2(live);
if (len > keys.length) {
newKeys = new int[keys.length*2];
newObjs = new Ref[objs.length*2];
} else {
newKeys = keys;
newObjs = objs;
}
int j = 0;
for (int i = 0; i < keys.length; i++) {
if (objs[i] != null) {
newKeys[j] = keys[i];
newObjs[j] = objs[i];
j++;
}
}
for (int i = j; i < newKeys.length; i++) {
newKeys[i] = 0;
newObjs[i] = null;
}
keys = newKeys;
objs = newObjs;
next = j;
if (live != next) {
throw new RuntimeException("bad state: live="+live+", next="+next);
}
}
private static int roundPow2(int x) {
int p = 1;
while (p < x) {
p *= 2;
}
return p;
}
}
}