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;
|
||||
|
||||
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> {
|
||||
private RemoveCallback firstCallback;
|
||||
private RemoveCallback lastCallback;
|
||||
|
||||
public TReference<T> poll() {
|
||||
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;
|
||||
}
|
||||
|
||||
class WaitHandler implements PlatformRunnable, TThreadInterruptHandler, EventQueue.Event {
|
||||
static class WaitHandler implements PlatformRunnable, TThreadInterruptHandler, EventQueue.Event {
|
||||
AsyncCallback<Boolean> callback;
|
||||
boolean complete;
|
||||
int timerId;
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package org.teavm.backend.javascript.intrinsics.ref;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
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 REGISTRY_FIELD = new FieldReference(ReferenceQueue.class.getName(),
|
||||
"registry");
|
||||
private static final MethodReference REPORT_METHOD = new MethodReference(ReferenceQueue.class,
|
||||
"reportNext", Reference.class, boolean.class);
|
||||
|
||||
@Override
|
||||
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 {
|
||||
writer.append(context.getParameterName(0)).append(".").appendField(INNER_FIELD).ws().append("=")
|
||||
.ws().append("[];").softNewLine();
|
||||
|
||||
writer.append(context.getParameterName(0)).append(".").appendField(REGISTRY_FIELD).ws().append("=")
|
||||
.ws().append("new $rt_globals.FinalizationRegistry(x").ws().append("=>").ws()
|
||||
.append(context.getParameterName(0)).appendField(INNER_FIELD)
|
||||
.append(".push(x));").softNewLine();
|
||||
.ws().append("new $rt_globals.FinalizationRegistry(ref").ws().append("=>").appendBlockStart();
|
||||
writer.appendIf().append("!").appendMethodBody(REPORT_METHOD).append("(")
|
||||
.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 {
|
||||
|
|
|
@ -20,31 +20,31 @@ import java.lang.ref.ReferenceQueue;
|
|||
import java.lang.ref.WeakReference;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WeakReferenceDependencyListener extends AbstractDependencyListener {
|
||||
private DependencyNode initRef;
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
initRef = agent.createNode();
|
||||
if (method.getMethod().getOwnerName().equals(WeakReference.class.getName())) {
|
||||
referenceMethodReached(agent, method);
|
||||
} else if (method.getMethod().getOwnerName().equals(ReferenceQueue.class.getName())) {
|
||||
agent.linkField(new FieldReference(ReferenceQueue.class.getName(), "inner"));
|
||||
agent.linkField(new FieldReference(ReferenceQueue.class.getName(), "registry"));
|
||||
queueMethodReached(agent, method);
|
||||
}
|
||||
}
|
||||
|
||||
private void referenceMethodReached(DependencyAgent agent, MethodDependency method) {
|
||||
switch (method.getMethod().getName()) {
|
||||
case "<init>": {
|
||||
if (method.getParameterCount() == 2) {
|
||||
if (method.getParameterCount() == 3) {
|
||||
var field = agent.linkField(new FieldReference(method.getMethod().getOwnerName(), "value"));
|
||||
method.getVariable(1).connect(field.getValue());
|
||||
var pollResult = agent
|
||||
.linkMethod(new MethodReference(ReferenceQueue.class, "poll", Reference.class))
|
||||
.getResult();
|
||||
method.getVariable(0).connect(pollResult);
|
||||
method.getVariable(2).connect(field.getValue());
|
||||
method.getVariable(1).connect(initRef);
|
||||
}
|
||||
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(".")
|
||||
.appendField(new FieldReference(ReferenceQueue.class.getName(), "registry"))
|
||||
.append(".").append("register(").append(context.getParameterName(1))
|
||||
.append(",").ws().append("value);").softNewLine();
|
||||
.append(",").ws().append(context.getParameterName(0)).append(");").softNewLine();
|
||||
writer.appendBlockEnd();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import static org.junit.Assert.assertSame;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
@ -36,26 +37,29 @@ public class WeakReferenceTest {
|
|||
|
||||
@Test
|
||||
@Ignore
|
||||
public void deref() {
|
||||
public void deref() throws InterruptedException {
|
||||
var ref = createAndTestRef(null);
|
||||
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
lastNode = createNodes(18);
|
||||
lastNode = createNodes(20);
|
||||
Thread.sleep(1);
|
||||
if (ref.get() == null) {
|
||||
break;
|
||||
}
|
||||
assertNotNull(lastNode);
|
||||
}
|
||||
assertNull(ref.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void refQueue() {
|
||||
public void refQueue() throws InterruptedException {
|
||||
var queue = new ReferenceQueue<>();
|
||||
var ref = createAndTestRef(queue);
|
||||
var hasValue = false;
|
||||
for (var i = 0; i < 100; ++i) {
|
||||
lastNode = createNodes(18);
|
||||
lastNode = createNodes(20);
|
||||
Thread.sleep(1);
|
||||
var polledRef = queue.poll();
|
||||
if (polledRef != null) {
|
||||
hasValue = true;
|
||||
|
@ -64,13 +68,41 @@ public class WeakReferenceTest {
|
|||
} else {
|
||||
assertNotNull(ref.get());
|
||||
}
|
||||
assertNotNull(lastNode);
|
||||
}
|
||||
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) {
|
||||
var obj = new byte[4 * 1024 * 1024];
|
||||
var ref = new WeakReference<Object>(obj, queue);
|
||||
var obj = new Object();
|
||||
var ref = new WeakReference<>(obj, queue);
|
||||
assertSame(obj, ref.get());
|
||||
return ref;
|
||||
}
|
||||
|
@ -96,7 +128,6 @@ public class WeakReferenceTest {
|
|||
private class Node {
|
||||
Node left;
|
||||
Node right;
|
||||
byte[] data = new byte[64];
|
||||
|
||||
Node(Node left, Node right) {
|
||||
this.left = left;
|
||||
|
|
Loading…
Reference in New Issue
Block a user