mirror of
https://github.com/PaperMC/Velocity.git
synced 2026-02-17 14:37:43 +01:00
Further API tweaks
This commit is contained in:
@@ -137,7 +137,15 @@ shadowJar {
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Object2Reference*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Object2Short*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*ObjectRB*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Byte*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Char*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Double*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Float*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Short*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Long*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Object*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Reference*'
|
||||
exclude 'it/unimi/dsi/fastutil/objects/*Reference2Boolean*'
|
||||
exclude 'it/unimi/dsi/fastutil/shorts/**'
|
||||
exclude 'org/checkerframework/checker/**'
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@ import com.velocitypowered.api.proxy.ProxyServer;
|
||||
import com.velocitypowered.api.proxy.server.RegisteredServer;
|
||||
import com.velocitypowered.api.util.ProxyVersion;
|
||||
import com.velocitypowered.proxy.VelocityServer;
|
||||
import com.velocitypowered.proxy.util.InformationUtils;
|
||||
import java.net.ConnectException;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.annotations.Expose;
|
||||
import com.velocitypowered.api.proxy.config.ProxyConfig;
|
||||
import com.velocitypowered.api.util.Favicon;
|
||||
import com.velocitypowered.proxy.util.AddressUtil;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@@ -41,10 +41,9 @@ import com.velocitypowered.proxy.network.java.serialization.brigadier.ArgumentPr
|
||||
import com.velocitypowered.proxy.network.packet.Packet;
|
||||
import com.velocitypowered.proxy.network.packet.PacketReader;
|
||||
import com.velocitypowered.proxy.network.packet.PacketWriter;
|
||||
import com.velocitypowered.proxy.util.collect.IdentityHashStrategy;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntLinkedOpenCustomHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Object2IntMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntLinkedOpenHashMap;
|
||||
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Arrays;
|
||||
import java.util.Deque;
|
||||
@@ -118,8 +117,8 @@ public class ClientboundAvailableCommandsPacket implements Packet {
|
||||
public void encode(ByteBuf buf, ProtocolVersion protocolVersion) {
|
||||
// Assign all the children an index.
|
||||
Deque<CommandNode<CommandSource>> childrenQueue = new ArrayDeque<>(ImmutableList.of(rootNode));
|
||||
Object2IntMap<CommandNode<CommandSource>> idMappings = new Object2IntLinkedOpenCustomHashMap<>(
|
||||
IdentityHashStrategy.instance());
|
||||
Reference2IntMap<CommandNode<CommandSource>> idMappings =
|
||||
new Reference2IntLinkedOpenHashMap<>();
|
||||
while (!childrenQueue.isEmpty()) {
|
||||
CommandNode<CommandSource> child = childrenQueue.poll();
|
||||
if (!idMappings.containsKey(child)) {
|
||||
@@ -137,7 +136,7 @@ public class ClientboundAvailableCommandsPacket implements Packet {
|
||||
}
|
||||
|
||||
private static void serializeNode(CommandNode<CommandSource> node, ByteBuf buf,
|
||||
Object2IntMap<CommandNode<CommandSource>> idMappings) {
|
||||
Reference2IntMap<CommandNode<CommandSource>> idMappings) {
|
||||
byte flags = 0;
|
||||
if (node.getRedirect() != null) {
|
||||
flags |= FLAG_IS_REDIRECT;
|
||||
|
||||
@@ -21,7 +21,7 @@ import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.VerifyException;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.network.ProtocolVersion;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.JavaPlayerIdentity;
|
||||
import com.velocitypowered.proxy.network.ProtocolUtils;
|
||||
import com.velocitypowered.proxy.network.java.packet.JavaPacketHandler;
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.velocitypowered.proxy.network.java.packet.JavaPacketHandler;
|
||||
import com.velocitypowered.proxy.network.packet.Packet;
|
||||
import com.velocitypowered.proxy.network.packet.PacketReader;
|
||||
import com.velocitypowered.proxy.network.packet.PacketWriter;
|
||||
import com.velocitypowered.proxy.util.DurationUtils;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import java.util.Arrays;
|
||||
import net.kyori.adventure.title.Title;
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.velocitypowered.api.proxy.player.TabList;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.TabList;
|
||||
import com.velocitypowered.api.proxy.player.java.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.JavaPlayerIdentity;
|
||||
import com.velocitypowered.proxy.connection.MinecraftConnection;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.velocitypowered.api.proxy.player.TabList;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.TabList;
|
||||
import com.velocitypowered.api.proxy.player.java.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.JavaPlayerIdentity;
|
||||
import com.velocitypowered.proxy.network.java.packet.clientbound.ClientboundPlayerListItemPacket;
|
||||
import net.kyori.adventure.text.Component;
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.JavaPlayerIdentity;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
package com.velocitypowered.proxy.tablist;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.velocitypowered.api.proxy.player.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.TabListEntry;
|
||||
import com.velocitypowered.api.proxy.player.java.JavaPlayerIdentity;
|
||||
import com.velocitypowered.proxy.connection.client.ConnectedPlayer;
|
||||
import com.velocitypowered.proxy.network.java.packet.clientbound.ClientboundPlayerListItemPacket;
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util;
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
/**
|
||||
* Provides utility functions for working with durations.
|
||||
*/
|
||||
public final class DurationUtils {
|
||||
private static final long ONE_TICK_IN_MILLISECONDS = 50;
|
||||
|
||||
private DurationUtils() {
|
||||
throw new AssertionError("Instances of this class should not be created.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the given duration to Minecraft ticks.
|
||||
*
|
||||
* @param duration the duration to convert into Minecraft ticks
|
||||
* @return the duration represented as the number of Minecraft ticks
|
||||
*/
|
||||
public static long toTicks(Duration duration) {
|
||||
return duration.toMillis() / ONE_TICK_IN_MILLISECONDS;
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.Iterables;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class FileSystemUtils {
|
||||
|
||||
/**
|
||||
* Visits the resources at the given {@link Path} within the resource
|
||||
* path of the given {@link Class}.
|
||||
*
|
||||
* @param target The target class of the resource path to scan
|
||||
* @param consumer The consumer to visit the resolved path
|
||||
* @param firstPathComponent First path component
|
||||
* @param remainingPathComponents Remaining path components
|
||||
*/
|
||||
@SuppressFBWarnings({"RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE"})
|
||||
public static boolean visitResources(Class<?> target, Consumer<Path> consumer,
|
||||
String firstPathComponent, String... remainingPathComponents)
|
||||
throws IOException {
|
||||
final URL knownResource = FileSystemUtils.class.getClassLoader()
|
||||
.getResource("default-velocity.toml");
|
||||
if (knownResource == null) {
|
||||
throw new IllegalStateException(
|
||||
"default-velocity.toml does not exist, don't know where we are");
|
||||
}
|
||||
if (knownResource.getProtocol().equals("jar")) {
|
||||
// Running from a JAR
|
||||
String jarPathRaw = Iterables.get(Splitter.on('!').split(knownResource.toString()), 0);
|
||||
URI path = URI.create(jarPathRaw + "!/");
|
||||
|
||||
try (FileSystem fileSystem = FileSystems.newFileSystem(path, Map.of("create", "true"))) {
|
||||
Path toVisit = fileSystem.getPath(firstPathComponent, remainingPathComponents);
|
||||
if (Files.exists(toVisit)) {
|
||||
consumer.accept(toVisit);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Running from the file system
|
||||
URI uri;
|
||||
List<String> componentList = new ArrayList<>();
|
||||
componentList.add(firstPathComponent);
|
||||
componentList.addAll(Arrays.asList(remainingPathComponents));
|
||||
|
||||
try {
|
||||
URL url = target.getClassLoader().getResource(String.join("/", componentList));
|
||||
if (url == null) {
|
||||
return false;
|
||||
}
|
||||
uri = url.toURI();
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
consumer.accept(Paths.get(uri));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util.collect;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ForwardingSet;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* An unsynchronized collection that puts an upper bound on the size of the collection.
|
||||
*/
|
||||
public final class CappedSet<T> extends ForwardingSet<T> {
|
||||
|
||||
private final Set<T> delegate;
|
||||
private final int upperSize;
|
||||
|
||||
private CappedSet(Set<T> delegate, int upperSize) {
|
||||
this.delegate = delegate;
|
||||
this.upperSize = upperSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a capped collection backed by a {@link HashSet}.
|
||||
* @param maxSize the maximum size of the collection
|
||||
* @param <T> the type of elements in the collection
|
||||
* @return the new collection
|
||||
*/
|
||||
public static <T> Set<T> create(int maxSize) {
|
||||
return new CappedSet<>(new HashSet<>(), maxSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<T> delegate() {
|
||||
return delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean add(T element) {
|
||||
if (this.delegate.size() >= upperSize) {
|
||||
Preconditions.checkState(this.delegate.contains(element),
|
||||
"collection is too large (%s >= %s)",
|
||||
this.delegate.size(), this.upperSize);
|
||||
return false;
|
||||
}
|
||||
return this.delegate.add(element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addAll(Collection<? extends T> collection) {
|
||||
return this.standardAddAll(collection);
|
||||
}
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util.collect;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* An immutable map of {@link Enum} entries to {@code int}s.
|
||||
* @param <E> the enum type
|
||||
*/
|
||||
public final class Enum2IntMap<E extends Enum<E>> {
|
||||
private final int[] mappings;
|
||||
|
||||
private Enum2IntMap(int[] mappings) {
|
||||
this.mappings = mappings;
|
||||
}
|
||||
|
||||
public int get(E key) {
|
||||
return mappings[key.ordinal()];
|
||||
}
|
||||
|
||||
public static class Builder<E extends Enum<E>> {
|
||||
private final int[] mappings;
|
||||
private final EnumSet<E> populated;
|
||||
private int defaultValue = -1;
|
||||
|
||||
public Builder(Class<E> klazz) {
|
||||
this.mappings = new int[klazz.getEnumConstants().length];
|
||||
this.populated = EnumSet.noneOf(klazz);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a mapping to the map.
|
||||
* @param key the key to use
|
||||
* @param value the value to associate with the key
|
||||
* @return {@code this}, for chaining
|
||||
*/
|
||||
public Builder<E> put(E key, int value) {
|
||||
this.mappings[key.ordinal()] = value;
|
||||
this.populated.add(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<E> remove(E key) {
|
||||
this.populated.remove(key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder<E> defaultValue(int defaultValue) {
|
||||
this.defaultValue = defaultValue;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches a mapping from the map.
|
||||
* @param key the key to use
|
||||
* @return the value in the map
|
||||
*/
|
||||
public int get(E key) {
|
||||
if (this.populated.contains(key)) {
|
||||
return this.mappings[key.ordinal()];
|
||||
}
|
||||
return this.defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the map.
|
||||
* @return the built map
|
||||
*/
|
||||
public Enum2IntMap<E> build() {
|
||||
for (E unpopulated : EnumSet.complementOf(this.populated)) {
|
||||
this.mappings[unpopulated.ordinal()] = this.defaultValue;
|
||||
}
|
||||
return new Enum2IntMap<>(this.mappings.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util.collect;
|
||||
|
||||
import it.unimi.dsi.fastutil.Hash.Strategy;
|
||||
|
||||
public final class IdentityHashStrategy<T> implements Strategy<T> {
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private static final IdentityHashStrategy INSTANCE = new IdentityHashStrategy();
|
||||
|
||||
public static <T> Strategy<T> instance() {
|
||||
//noinspection unchecked
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(T o) {
|
||||
return System.identityHashCode(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(T a, T b) {
|
||||
return a == b;
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 Velocity Contributors
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.velocitypowered.proxy.util.concurrent;
|
||||
|
||||
/**
|
||||
* A class that guarantees that a given initialization shall only occur once. The implementation
|
||||
* is (almost) a direct Java port of the Go {@code sync.Once} type (see the
|
||||
* <a href="https://golang.org/pkg/sync/#Once">Go documentation</a>) and thus has similar
|
||||
* semantics.
|
||||
*/
|
||||
public final class Once {
|
||||
private static final int NOT_STARTED = 0;
|
||||
private static final int COMPLETED = 1;
|
||||
|
||||
private volatile int completed = NOT_STARTED;
|
||||
private final Object lock = new Object();
|
||||
|
||||
/**
|
||||
* Calls {@code runnable.run()} exactly once if this instance is being called for the first time,
|
||||
* otherwise the invocation shall wait until {@code runnable.run()} completes. The first runnable
|
||||
* used when this function is called is run. Future calls to this method once the initial
|
||||
* runnable completes are no-ops - a new instance should be used instead.
|
||||
*
|
||||
* @param runnable the runnable to run
|
||||
*/
|
||||
public void run(Runnable runnable) {
|
||||
if (completed == NOT_STARTED) {
|
||||
slowRun(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private void slowRun(Runnable runnable) {
|
||||
synchronized (lock) {
|
||||
if (completed == NOT_STARTED) {
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
completed = COMPLETED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user