2015-04-28 12:29:48 -04:00
|
|
|
// 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.
|
|
|
|
|
2014-07-31 08:24:05 -04:00
|
|
|
package go;
|
|
|
|
|
2015-07-16 00:23:53 -04:00
|
|
|
import android.app.Application;
|
|
|
|
import android.content.Context;
|
2014-07-31 08:24:05 -04:00
|
|
|
import android.util.Log;
|
|
|
|
import android.util.SparseArray;
|
|
|
|
import android.util.SparseIntArray;
|
|
|
|
|
|
|
|
import java.util.concurrent.ExecutorService;
|
|
|
|
import java.util.concurrent.Executors;
|
|
|
|
|
|
|
|
// Seq is a sequence of machine-dependent encoded values.
|
|
|
|
// Used by automatically generated language bindings to talk to Go.
|
|
|
|
public class Seq {
|
2015-06-27 18:35:21 -04:00
|
|
|
static {
|
2015-07-01 01:18:25 -04:00
|
|
|
// Look for the shim class auto-generated by gomobile bind.
|
|
|
|
// Its only purpose is to call System.loadLibrary.
|
|
|
|
try {
|
|
|
|
Class.forName("go.LoadJNI");
|
|
|
|
} catch (ClassNotFoundException e) {
|
|
|
|
// Ignore, assume the user will load JNI for it.
|
2015-07-10 16:47:46 -06:00
|
|
|
Log.w("GoSeq", "LoadJNI class not found");
|
2015-07-01 01:18:25 -04:00
|
|
|
}
|
|
|
|
|
2015-07-16 00:23:53 -04:00
|
|
|
try {
|
|
|
|
// TODO(hyangah): check proguard rule.
|
2015-07-22 14:21:13 -04:00
|
|
|
Application appl = (Application)Class.forName("android.app.AppGlobals").getMethod("getInitialApplication").invoke(null);
|
2015-07-16 00:23:53 -04:00
|
|
|
Context ctx = appl.getApplicationContext();
|
|
|
|
setContext(ctx);
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
Log.w("GoSeq", "Global context not found:" + e);
|
|
|
|
}
|
|
|
|
|
2015-06-27 18:35:21 -04:00
|
|
|
initSeq();
|
|
|
|
new Thread("GoSeq") {
|
|
|
|
public void run() { Seq.receive(); }
|
|
|
|
}.start();
|
|
|
|
}
|
|
|
|
|
2014-07-31 08:24:05 -04:00
|
|
|
@SuppressWarnings("UnusedDeclaration")
|
|
|
|
private long memptr; // holds C-allocated pointer
|
|
|
|
|
|
|
|
public Seq() {
|
|
|
|
ensure(64);
|
|
|
|
}
|
|
|
|
|
2015-07-16 00:23:53 -04:00
|
|
|
static native void setContext(Context ctx);
|
|
|
|
|
2014-07-31 08:24:05 -04:00
|
|
|
// Ensure that at least size bytes can be written to the Seq.
|
|
|
|
// Any existing data in the buffer is preserved.
|
|
|
|
public native void ensure(int size);
|
|
|
|
|
|
|
|
// Moves the internal buffer offset back to zero.
|
|
|
|
// Length and contents are maintained. Data can be read after a reset.
|
|
|
|
public native void resetOffset();
|
|
|
|
|
|
|
|
public native void log(String label);
|
|
|
|
|
2015-05-14 15:46:09 -04:00
|
|
|
public native boolean readBool();
|
2014-07-31 08:24:05 -04:00
|
|
|
public native byte readInt8();
|
|
|
|
public native short readInt16();
|
|
|
|
public native int readInt32();
|
|
|
|
public native long readInt64();
|
|
|
|
public long readInt() { return readInt64(); }
|
|
|
|
|
|
|
|
public native float readFloat32();
|
|
|
|
public native double readFloat64();
|
|
|
|
public native String readUTF16();
|
2015-04-06 16:43:18 -04:00
|
|
|
public String readString() { return readUTF16(); }
|
2014-07-31 08:24:05 -04:00
|
|
|
public native byte[] readByteArray();
|
|
|
|
|
2015-05-14 15:46:09 -04:00
|
|
|
public native void writeBool(boolean v);
|
2014-07-31 08:24:05 -04:00
|
|
|
public native void writeInt8(byte v);
|
|
|
|
public native void writeInt16(short v);
|
|
|
|
public native void writeInt32(int v);
|
|
|
|
public native void writeInt64(long v);
|
|
|
|
public void writeInt(long v) { writeInt64(v); }
|
|
|
|
|
|
|
|
public native void writeFloat32(float v);
|
|
|
|
public native void writeFloat64(double v);
|
|
|
|
public native void writeUTF16(String v);
|
2015-04-06 16:43:18 -04:00
|
|
|
public void writeString(String v) { writeUTF16(v); }
|
2014-07-31 08:24:05 -04:00
|
|
|
public native void writeByteArray(byte[] v);
|
|
|
|
|
|
|
|
public void writeRef(Ref ref) {
|
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
|
|
|
tracker.inc(ref);
|
2014-07-31 08:24:05 -04:00
|
|
|
writeInt32(ref.refnum);
|
|
|
|
}
|
|
|
|
|
|
|
|
public Ref readRef() {
|
|
|
|
int refnum = readInt32();
|
|
|
|
return tracker.get(refnum);
|
|
|
|
}
|
|
|
|
|
2015-05-21 12:32:58 -04:00
|
|
|
static native void initSeq();
|
|
|
|
|
2014-07-31 08:24:05 -04:00
|
|
|
// Informs the Go ref tracker that Java is done with this ref.
|
|
|
|
static native void destroyRef(int refnum);
|
|
|
|
|
|
|
|
// createRef creates a Ref to a Java object.
|
|
|
|
public static Ref createRef(Seq.Object o) {
|
|
|
|
return tracker.createRef(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
// sends a function invocation request to Go.
|
|
|
|
//
|
|
|
|
// Blocks until the function completes.
|
|
|
|
// If the request is for a method, the first element in src is
|
|
|
|
// a Ref to the receiver.
|
|
|
|
public static native void send(String descriptor, int code, Seq src, Seq dst);
|
|
|
|
|
|
|
|
// recv returns the next request from Go for a Java call.
|
|
|
|
static native void recv(Seq in, Receive params);
|
|
|
|
|
|
|
|
// recvRes sends the result of a Java call back to Go.
|
|
|
|
static native void recvRes(int handle, Seq out);
|
|
|
|
|
|
|
|
static final class Receive {
|
|
|
|
int refnum;
|
|
|
|
int code;
|
|
|
|
int handle;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void finalize() throws Throwable {
|
|
|
|
super.finalize();
|
|
|
|
free();
|
|
|
|
}
|
|
|
|
private native void free();
|
|
|
|
|
|
|
|
private static final ExecutorService receivePool = Executors.newCachedThreadPool();
|
|
|
|
|
|
|
|
// receive listens for callback requests from Go, invokes them on a thread
|
|
|
|
// pool and sends the responses.
|
|
|
|
public static void receive() {
|
|
|
|
Seq.Receive params = new Seq.Receive();
|
|
|
|
while (true) {
|
|
|
|
final Seq in = new Seq();
|
|
|
|
Seq.recv(in, params);
|
|
|
|
|
|
|
|
final int code = params.code;
|
|
|
|
final int handle = params.handle;
|
|
|
|
final int refnum = params.refnum;
|
|
|
|
|
|
|
|
if (code == -1) {
|
|
|
|
// Special signal from seq.FinalizeRef.
|
|
|
|
tracker.dec(refnum);
|
|
|
|
Seq out = new Seq();
|
|
|
|
Seq.recvRes(handle, out);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
receivePool.execute(new Runnable() {
|
|
|
|
public void run() {
|
|
|
|
Ref r = tracker.get(refnum);
|
|
|
|
Seq out = new Seq();
|
|
|
|
r.obj.call(code, in, out);
|
|
|
|
Seq.recvRes(handle, out);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// An Object is a Java object that matches a Go object.
|
|
|
|
// The implementation of the object may be in either Java or Go,
|
|
|
|
// with a proxy instance in the other language passing calls
|
|
|
|
// through to the other language.
|
|
|
|
//
|
|
|
|
// Don't implement an Object directly. Instead, look for the
|
|
|
|
// generated abstract Stub.
|
|
|
|
public interface Object {
|
|
|
|
public Ref ref();
|
|
|
|
public void call(int code, Seq in, Seq out);
|
|
|
|
}
|
|
|
|
|
|
|
|
// A Ref is an object tagged with an integer for passing back and
|
|
|
|
// forth across the language boundary.
|
|
|
|
//
|
|
|
|
// A Ref may represent either an instance of a Java Object subclass,
|
|
|
|
// or an instance of a Go object. The explicit allocation of a Ref
|
|
|
|
// is used to pin Go object instances when they are passed to Java.
|
|
|
|
// The Go Seq library maintains a reference to the instance in a map
|
|
|
|
// keyed by the Ref number. When the JVM calls finalize, we ask Go
|
|
|
|
// to clear the entry in the map.
|
|
|
|
public static final class Ref {
|
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
|
|
|
// refnum < 0: Go object tracked by Java
|
|
|
|
// refnum > 0: Java object tracked by Go
|
2014-07-31 08:24:05 -04:00
|
|
|
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
|
|
|
|
|
|
|
int refcnt; // for Java obj: track how many times sent to Go.
|
|
|
|
|
|
|
|
public Seq.Object obj; // for Java obj: pointers to the Java obj.
|
2014-07-31 08:24:05 -04:00
|
|
|
|
|
|
|
private Ref(int refnum, Seq.Object o) {
|
|
|
|
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;
|
2014-07-31 08:24:05 -04:00
|
|
|
this.obj = o;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void finalize() throws Throwable {
|
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) {
|
|
|
|
// Go object: signal Go to decrement the reference count.
|
|
|
|
Seq.destroyRef(refnum);
|
|
|
|
}
|
2014-07-31 08:24:05 -04:00
|
|
|
super.finalize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static final RefTracker tracker = new RefTracker();
|
|
|
|
|
|
|
|
static final class RefTracker {
|
|
|
|
// 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 = 42; // 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 SparseArray<Ref> javaObjs = new SparseArray<Ref>();
|
|
|
|
|
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.
|
|
|
|
synchronized void inc(Ref ref) {
|
|
|
|
int refnum = ref.refnum;
|
|
|
|
if (refnum <= 0) {
|
|
|
|
// We don't keep track of the Go object.
|
|
|
|
return;
|
2014-07-31 08:24:05 -04:00
|
|
|
}
|
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
|
|
|
// Count Java objects passed to Go.
|
|
|
|
if (ref.refcnt == Integer.MAX_VALUE) {
|
2014-07-31 08:24:05 -04:00
|
|
|
throw new RuntimeException("refnum " + refnum + " overflow");
|
|
|
|
}
|
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
|
|
|
ref.refcnt++;
|
|
|
|
Ref obj = javaObjs.get(refnum);
|
|
|
|
if (obj == null) {
|
|
|
|
javaObjs.put(refnum, ref);
|
|
|
|
}
|
2014-07-31 08:24:05 -04:00
|
|
|
}
|
|
|
|
|
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.
|
2014-07-31 08:24:05 -04:00
|
|
|
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.
|
2015-07-10 16:47:46 -06:00
|
|
|
Log.wtf("GoSeq", "dec request for Go object "+ refnum);
|
2014-07-31 08:24:05 -04:00
|
|
|
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);
|
2014-07-31 08:24:05 -04:00
|
|
|
}
|
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);
|
2014-07-31 08:24:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
synchronized Ref createRef(Seq.Object o) {
|
|
|
|
// TODO(crawshaw): use single Ref for null.
|
|
|
|
if (next == Integer.MAX_VALUE) {
|
|
|
|
throw new RuntimeException("createRef overflow for " + o);
|
|
|
|
}
|
|
|
|
int refnum = next++;
|
|
|
|
Ref ref = new Ref(refnum, o);
|
|
|
|
javaObjs.put(refnum, ref);
|
|
|
|
return ref;
|
|
|
|
}
|
|
|
|
|
|
|
|
// get returns an existing Ref to either a Java or Go object.
|
|
|
|
// It may be the first time we have seen the Go object.
|
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
|
|
|
//
|
|
|
|
// 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.
|
2014-07-31 08:24:05 -04:00
|
|
|
synchronized Ref get(int refnum) {
|
|
|
|
if (refnum > 0) {
|
|
|
|
Ref ref = javaObjs.get(refnum);
|
|
|
|
if (ref == null) {
|
|
|
|
throw new RuntimeException("unknown java Ref: "+refnum);
|
|
|
|
}
|
|
|
|
return ref;
|
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
|
|
|
} else {
|
|
|
|
// Go object.
|
|
|
|
return new Ref(refnum, null);
|
2014-07-31 08:24:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|