mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
classlib: support ReferenceQueue.remove
This commit is contained in:
parent
df40dedba3
commit
9ecb3ad817
|
@ -15,8 +15,128 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang.ref;
|
package org.teavm.classlib.java.lang.ref;
|
||||||
|
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
|
import org.teavm.classlib.java.lang.TThread;
|
||||||
|
import org.teavm.classlib.java.lang.TThreadInterruptHandler;
|
||||||
|
import org.teavm.interop.Async;
|
||||||
|
import org.teavm.interop.AsyncCallback;
|
||||||
|
import org.teavm.platform.Platform;
|
||||||
|
import org.teavm.platform.PlatformRunnable;
|
||||||
|
import org.teavm.runtime.EventQueue;
|
||||||
|
|
||||||
public class TReferenceQueue<T> {
|
public class TReferenceQueue<T> {
|
||||||
|
private RemoveCallback firstCallback;
|
||||||
|
private RemoveCallback lastCallback;
|
||||||
|
|
||||||
public TReference<T> poll() {
|
public TReference<T> poll() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TReference<T> remove() throws InterruptedException {
|
||||||
|
return remove(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Async
|
||||||
|
public native TReference<T> remove(long timeout) throws InterruptedException;
|
||||||
|
|
||||||
|
public void remove(long timeout, AsyncCallback<TReference<T>> callback) {
|
||||||
|
var ref = poll();
|
||||||
|
if (ref != null) {
|
||||||
|
callback.complete(ref);
|
||||||
|
} else {
|
||||||
|
var callbackWrapper = new RemoveCallback(callback);
|
||||||
|
if (timeout != 0) {
|
||||||
|
callbackWrapper.id = PlatformDetector.isLowLevel()
|
||||||
|
? EventQueue.offer(callbackWrapper, timeout + System.currentTimeMillis())
|
||||||
|
: Platform.schedule(callbackWrapper, (int) timeout);
|
||||||
|
}
|
||||||
|
TThread.currentThread().interruptHandler = callbackWrapper;
|
||||||
|
registerCallback(callbackWrapper);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerCallback(RemoveCallback callback) {
|
||||||
|
callback.prev = lastCallback;
|
||||||
|
if (lastCallback != null) {
|
||||||
|
lastCallback.next = callback;
|
||||||
|
} else {
|
||||||
|
firstCallback = callback;
|
||||||
|
}
|
||||||
|
lastCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean reportNext(TReference<T> ref) {
|
||||||
|
if (firstCallback == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var callback = firstCallback;
|
||||||
|
callback.complete(ref);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RemoveCallback implements EventQueue.Event, PlatformRunnable, AsyncCallback<TReference<T>>,
|
||||||
|
TThreadInterruptHandler {
|
||||||
|
RemoveCallback next;
|
||||||
|
RemoveCallback prev;
|
||||||
|
int id;
|
||||||
|
AsyncCallback<TReference<T>> callback;
|
||||||
|
|
||||||
|
RemoveCallback(AsyncCallback<TReference<T>> callback) {
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (PlatformDetector.isLowLevel()) {
|
||||||
|
EventQueue.kill(id);
|
||||||
|
} else {
|
||||||
|
Platform.killSchedule(id);
|
||||||
|
}
|
||||||
|
complete(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void complete(TReference<T> result) {
|
||||||
|
var callback = this.callback;
|
||||||
|
if (callback != null) {
|
||||||
|
remove();
|
||||||
|
callback.complete(result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(Throwable e) {
|
||||||
|
var callback = this.callback;
|
||||||
|
if (callback != null) {
|
||||||
|
remove();
|
||||||
|
callback.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void interrupted() {
|
||||||
|
var callback = this.callback;
|
||||||
|
if (callback != null) {
|
||||||
|
remove();
|
||||||
|
callback.error(new InterruptedException());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void remove() {
|
||||||
|
TThread.currentThread().interruptHandler = null;
|
||||||
|
callback = null;
|
||||||
|
if (prev != null) {
|
||||||
|
prev.next = next;
|
||||||
|
} else {
|
||||||
|
firstCallback = next;
|
||||||
|
}
|
||||||
|
if (next != null) {
|
||||||
|
next.prev = prev;
|
||||||
|
} else {
|
||||||
|
lastCallback = prev;
|
||||||
|
}
|
||||||
|
next = null;
|
||||||
|
prev = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -447,7 +447,7 @@ public class TArrayBlockingQueue<E> extends TAbstractQueue<E> implements TBlocki
|
||||||
TThread.currentThread().interruptHandler = handler;
|
TThread.currentThread().interruptHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
class WaitHandler implements PlatformRunnable, TThreadInterruptHandler, EventQueue.Event {
|
static class WaitHandler implements PlatformRunnable, TThreadInterruptHandler, EventQueue.Event {
|
||||||
AsyncCallback<Boolean> callback;
|
AsyncCallback<Boolean> callback;
|
||||||
boolean complete;
|
boolean complete;
|
||||||
int timerId;
|
int timerId;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.backend.javascript.intrinsics.ref;
|
package org.teavm.backend.javascript.intrinsics.ref;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.Reference;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
|
@ -27,6 +28,8 @@ public class ReferenceQueueGenerator implements Generator {
|
||||||
private static final FieldReference INNER_FIELD = new FieldReference(ReferenceQueue.class.getName(), "inner");
|
private static final FieldReference INNER_FIELD = new FieldReference(ReferenceQueue.class.getName(), "inner");
|
||||||
private static final FieldReference REGISTRY_FIELD = new FieldReference(ReferenceQueue.class.getName(),
|
private static final FieldReference REGISTRY_FIELD = new FieldReference(ReferenceQueue.class.getName(),
|
||||||
"registry");
|
"registry");
|
||||||
|
private static final MethodReference REPORT_METHOD = new MethodReference(ReferenceQueue.class,
|
||||||
|
"reportNext", Reference.class, boolean.class);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
|
@ -43,10 +46,14 @@ public class ReferenceQueueGenerator implements Generator {
|
||||||
private void generateInitMethod(GeneratorContext context, SourceWriter writer) throws IOException {
|
private void generateInitMethod(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||||
writer.append(context.getParameterName(0)).append(".").appendField(INNER_FIELD).ws().append("=")
|
writer.append(context.getParameterName(0)).append(".").appendField(INNER_FIELD).ws().append("=")
|
||||||
.ws().append("[];").softNewLine();
|
.ws().append("[];").softNewLine();
|
||||||
|
|
||||||
writer.append(context.getParameterName(0)).append(".").appendField(REGISTRY_FIELD).ws().append("=")
|
writer.append(context.getParameterName(0)).append(".").appendField(REGISTRY_FIELD).ws().append("=")
|
||||||
.ws().append("new $rt_globals.FinalizationRegistry(x").ws().append("=>").ws()
|
.ws().append("new $rt_globals.FinalizationRegistry(ref").ws().append("=>").appendBlockStart();
|
||||||
.append(context.getParameterName(0)).appendField(INNER_FIELD)
|
writer.appendIf().append("!").appendMethodBody(REPORT_METHOD).append("(")
|
||||||
.append(".push(x));").softNewLine();
|
.append(context.getParameterName(0)).append(",").ws().append("ref))").ws();
|
||||||
|
writer.append(context.getParameterName(0)).append(".").appendField(INNER_FIELD)
|
||||||
|
.append(".push(ref)").softNewLine();
|
||||||
|
writer.appendBlockEnd().append(");").softNewLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void generatePollMethod(GeneratorContext context, SourceWriter writer) throws IOException {
|
private void generatePollMethod(GeneratorContext context, SourceWriter writer) throws IOException {
|
||||||
|
|
|
@ -20,31 +20,31 @@ import java.lang.ref.ReferenceQueue;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.DependencyNode;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class WeakReferenceDependencyListener extends AbstractDependencyListener {
|
public class WeakReferenceDependencyListener extends AbstractDependencyListener {
|
||||||
|
private DependencyNode initRef;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
initRef = agent.createNode();
|
||||||
if (method.getMethod().getOwnerName().equals(WeakReference.class.getName())) {
|
if (method.getMethod().getOwnerName().equals(WeakReference.class.getName())) {
|
||||||
referenceMethodReached(agent, method);
|
referenceMethodReached(agent, method);
|
||||||
} else if (method.getMethod().getOwnerName().equals(ReferenceQueue.class.getName())) {
|
} else if (method.getMethod().getOwnerName().equals(ReferenceQueue.class.getName())) {
|
||||||
agent.linkField(new FieldReference(ReferenceQueue.class.getName(), "inner"));
|
queueMethodReached(agent, method);
|
||||||
agent.linkField(new FieldReference(ReferenceQueue.class.getName(), "registry"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void referenceMethodReached(DependencyAgent agent, MethodDependency method) {
|
private void referenceMethodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
switch (method.getMethod().getName()) {
|
switch (method.getMethod().getName()) {
|
||||||
case "<init>": {
|
case "<init>": {
|
||||||
if (method.getParameterCount() == 2) {
|
if (method.getParameterCount() == 3) {
|
||||||
var field = agent.linkField(new FieldReference(method.getMethod().getOwnerName(), "value"));
|
var field = agent.linkField(new FieldReference(method.getMethod().getOwnerName(), "value"));
|
||||||
method.getVariable(1).connect(field.getValue());
|
method.getVariable(2).connect(field.getValue());
|
||||||
var pollResult = agent
|
method.getVariable(1).connect(initRef);
|
||||||
.linkMethod(new MethodReference(ReferenceQueue.class, "poll", Reference.class))
|
|
||||||
.getResult();
|
|
||||||
method.getVariable(0).connect(pollResult);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -55,4 +55,23 @@ public class WeakReferenceDependencyListener extends AbstractDependencyListener
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void queueMethodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
switch (method.getMethod().getName()) {
|
||||||
|
case "poll":
|
||||||
|
initRef.connect(method.getResult());
|
||||||
|
break;
|
||||||
|
case "<init>":
|
||||||
|
agent.linkField(new FieldReference(ReferenceQueue.class.getName(), "inner"));
|
||||||
|
agent.linkField(new FieldReference(ReferenceQueue.class.getName(), "registry"));
|
||||||
|
break;
|
||||||
|
case "registerCallback": {
|
||||||
|
var reportMethod = agent.linkMethod(new MethodReference(ReferenceQueue.class,
|
||||||
|
"reportNext", Reference.class, boolean.class));
|
||||||
|
initRef.connect(reportMethod.getVariable(1));
|
||||||
|
reportMethod.use();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ public class WeakReferenceGenerator implements Generator {
|
||||||
writer.append(context.getParameterName(2)).append(".")
|
writer.append(context.getParameterName(2)).append(".")
|
||||||
.appendField(new FieldReference(ReferenceQueue.class.getName(), "registry"))
|
.appendField(new FieldReference(ReferenceQueue.class.getName(), "registry"))
|
||||||
.append(".").append("register(").append(context.getParameterName(1))
|
.append(".").append("register(").append(context.getParameterName(1))
|
||||||
.append(",").ws().append("value);").softNewLine();
|
.append(",").ws().append(context.getParameterName(0)).append(");").softNewLine();
|
||||||
writer.appendBlockEnd();
|
writer.appendBlockEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import java.lang.ref.ReferenceQueue;
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
@ -36,26 +37,29 @@ public class WeakReferenceTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void deref() {
|
public void deref() throws InterruptedException {
|
||||||
var ref = createAndTestRef(null);
|
var ref = createAndTestRef(null);
|
||||||
|
|
||||||
for (var i = 0; i < 100; ++i) {
|
for (var i = 0; i < 100; ++i) {
|
||||||
lastNode = createNodes(18);
|
lastNode = createNodes(20);
|
||||||
|
Thread.sleep(1);
|
||||||
if (ref.get() == null) {
|
if (ref.get() == null) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
assertNotNull(lastNode);
|
||||||
}
|
}
|
||||||
assertNull(ref.get());
|
assertNull(ref.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore
|
@Ignore
|
||||||
public void refQueue() {
|
public void refQueue() throws InterruptedException {
|
||||||
var queue = new ReferenceQueue<>();
|
var queue = new ReferenceQueue<>();
|
||||||
var ref = createAndTestRef(queue);
|
var ref = createAndTestRef(queue);
|
||||||
var hasValue = false;
|
var hasValue = false;
|
||||||
for (var i = 0; i < 100; ++i) {
|
for (var i = 0; i < 100; ++i) {
|
||||||
lastNode = createNodes(18);
|
lastNode = createNodes(20);
|
||||||
|
Thread.sleep(1);
|
||||||
var polledRef = queue.poll();
|
var polledRef = queue.poll();
|
||||||
if (polledRef != null) {
|
if (polledRef != null) {
|
||||||
hasValue = true;
|
hasValue = true;
|
||||||
|
@ -64,13 +68,41 @@ public class WeakReferenceTest {
|
||||||
} else {
|
} else {
|
||||||
assertNotNull(ref.get());
|
assertNotNull(ref.get());
|
||||||
}
|
}
|
||||||
|
assertNotNull(lastNode);
|
||||||
}
|
}
|
||||||
assertTrue(hasValue);
|
assertTrue(hasValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void queueRemove() throws InterruptedException {
|
||||||
|
var queue = new ReferenceQueue<>();
|
||||||
|
var ref = createAndTestRef(queue);
|
||||||
|
var threadQueue = new ArrayBlockingQueue<>(4);
|
||||||
|
var thread = new Thread(() -> {
|
||||||
|
try {
|
||||||
|
threadQueue.add(queue.remove());
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.setDaemon(true);
|
||||||
|
thread.start();
|
||||||
|
Object value = null;
|
||||||
|
for (var i = 0; i < 100; ++i) {
|
||||||
|
lastNode = createNodes(20);
|
||||||
|
Thread.sleep(1);
|
||||||
|
value = threadQueue.poll();
|
||||||
|
if (value != null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
assertNotNull(lastNode);
|
||||||
|
}
|
||||||
|
assertSame(ref, value);
|
||||||
|
}
|
||||||
|
|
||||||
private WeakReference<Object> createAndTestRef(ReferenceQueue<Object> queue) {
|
private WeakReference<Object> createAndTestRef(ReferenceQueue<Object> queue) {
|
||||||
var obj = new byte[4 * 1024 * 1024];
|
var obj = new Object();
|
||||||
var ref = new WeakReference<Object>(obj, queue);
|
var ref = new WeakReference<>(obj, queue);
|
||||||
assertSame(obj, ref.get());
|
assertSame(obj, ref.get());
|
||||||
return ref;
|
return ref;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +128,6 @@ public class WeakReferenceTest {
|
||||||
private class Node {
|
private class Node {
|
||||||
Node left;
|
Node left;
|
||||||
Node right;
|
Node right;
|
||||||
byte[] data = new byte[64];
|
|
||||||
|
|
||||||
Node(Node left, Node right) {
|
Node(Node left, Node right) {
|
||||||
this.left = left;
|
this.left = left;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user