diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java index 96f6dbc25..0b0799ea2 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/Task.java @@ -345,16 +345,11 @@ public abstract class Task { } private final ReadOnlyStringWrapper message = new ReadOnlyStringWrapper(this, "message", null); - private final InvocationDispatcher messageUpdate = InvocationDispatcher.runOn(Platform::runLater, message::set); public final ReadOnlyStringProperty messageProperty() { return message.getReadOnlyProperty(); } - protected final void updateMessage(String newMessage) { - messageUpdate.accept(newMessage); - } - public final T run() throws Exception { if (getSignificance().shouldLog()) LOG.trace("Executing task: " + getName()); diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java index a6cf8031b..cb3659116 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/InvocationDispatcher.java @@ -17,8 +17,9 @@ */ package org.jackhuang.hmcl.util; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; /// When [#accept(T)] is called, this class invokes the handler on another thread. @@ -28,6 +29,16 @@ import java.util.function.Consumer; /// @author yushijinhun public final class InvocationDispatcher implements Consumer { + private static final VarHandle PENDING_ARG_HANDLE; + static { + try { + PENDING_ARG_HANDLE = MethodHandles.lookup() + .findVarHandle(InvocationDispatcher.class, "pendingArg", Holder.class); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + /// @param executor The executor must dispatch all tasks to a single thread. public static InvocationDispatcher runOn(Executor executor, Consumer action) { return new InvocationDispatcher<>(executor, action); @@ -35,7 +46,10 @@ public final class InvocationDispatcher implements Consumer { private final Executor executor; private final Consumer action; - private final AtomicReference> pendingArg = new AtomicReference<>(); + + /// @see #PENDING_ARG_HANDLE + @SuppressWarnings("unused") + private volatile Holder pendingArg; private InvocationDispatcher(Executor executor, Consumer action) { this.executor = executor; @@ -44,12 +58,15 @@ public final class InvocationDispatcher implements Consumer { @Override public void accept(T t) { - if (pendingArg.getAndSet(new Holder<>(t)) == null) { + if (PENDING_ARG_HANDLE.getAndSet(this, new Holder<>(t)) == null) { executor.execute(() -> { + @SuppressWarnings("unchecked") + var holder = (Holder) PENDING_ARG_HANDLE.getAndSet(this, (Holder) null); + // If the executor supports multiple underlying threads, // we need to add synchronization, but for now we can omit it :) // synchronized (InvocationDispatcher.this) - action.accept(pendingArg.getAndSet(null).value); + action.accept(holder.value); }); } }