Mirror von
https://github.com/PaperMC/Velocity.git
synchronisiert 2024-11-17 05:20:14 +01:00
Merge branch 'dev/3.0.0' into dev/5.0.0
Dieser Commit ist enthalten in:
Commit
64693cc97d
@ -8,6 +8,7 @@
|
|||||||
package com.velocitypowered.api.plugin;
|
package com.velocitypowered.api.plugin;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A wrapper around a plugin loaded by the proxy.
|
* A wrapper around a plugin loaded by the proxy.
|
||||||
@ -29,4 +30,12 @@ public interface PluginContainer {
|
|||||||
default Optional<?> getInstance() {
|
default Optional<?> getInstance() {
|
||||||
return Optional.empty();
|
return Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an executor service for this plugin. The executor will use a cached
|
||||||
|
* thread pool.
|
||||||
|
*
|
||||||
|
* @return an {@link ExecutorService} associated with this plugin
|
||||||
|
*/
|
||||||
|
ExecutorService getExecutorService();
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,12 @@
|
|||||||
|
|
||||||
package com.velocitypowered.proxy.plugin.loader;
|
package com.velocitypowered.proxy.plugin.loader;
|
||||||
|
|
||||||
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
import com.velocitypowered.api.plugin.PluginContainer;
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginDescription;
|
import com.velocitypowered.api.plugin.PluginDescription;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements {@link PluginContainer}.
|
* Implements {@link PluginContainer}.
|
||||||
@ -28,6 +31,7 @@ public class VelocityPluginContainer implements PluginContainer {
|
|||||||
|
|
||||||
private final PluginDescription description;
|
private final PluginDescription description;
|
||||||
private Object instance;
|
private Object instance;
|
||||||
|
private volatile ExecutorService service;
|
||||||
|
|
||||||
public VelocityPluginContainer(PluginDescription description) {
|
public VelocityPluginContainer(PluginDescription description) {
|
||||||
this.description = description;
|
this.description = description;
|
||||||
@ -46,4 +50,24 @@ public class VelocityPluginContainer implements PluginContainer {
|
|||||||
public void setInstance(Object instance) {
|
public void setInstance(Object instance) {
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecutorService getExecutorService() {
|
||||||
|
if (this.service == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (this.service == null) {
|
||||||
|
String name = this.description.getName().orElse(this.description.getId());
|
||||||
|
this.service = Executors.unconfigurableExecutorService(
|
||||||
|
Executors.newCachedThreadPool(
|
||||||
|
new ThreadFactoryBuilder().setDaemon(true)
|
||||||
|
.setNameFormat(name + " - Task Executor #%d")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.service;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import com.velocitypowered.api.plugin.PluginDescription;
|
|||||||
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
import com.velocitypowered.api.plugin.annotation.DataDirectory;
|
||||||
import com.velocitypowered.api.proxy.ProxyServer;
|
import com.velocitypowered.api.proxy.ProxyServer;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -52,5 +53,7 @@ class VelocityPluginModule implements Module {
|
|||||||
.toInstance(basePluginPath.resolve(description.getId()));
|
.toInstance(basePluginPath.resolve(description.getId()));
|
||||||
binder.bind(PluginDescription.class).toInstance(description);
|
binder.bind(PluginDescription.class).toInstance(description);
|
||||||
binder.bind(PluginContainer.class).toInstance(pluginContainer);
|
binder.bind(PluginContainer.class).toInstance(pluginContainer);
|
||||||
|
|
||||||
|
binder.bind(ExecutorService.class).toProvider(pluginContainer::getExecutorService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,39 +24,39 @@ import com.google.common.collect.ImmutableList;
|
|||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import com.google.common.collect.Multimaps;
|
import com.google.common.collect.Multimaps;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import com.velocitypowered.api.plugin.PluginContainer;
|
||||||
import com.velocitypowered.api.plugin.PluginManager;
|
import com.velocitypowered.api.plugin.PluginManager;
|
||||||
import com.velocitypowered.api.scheduler.ScheduledTask;
|
import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||||
import com.velocitypowered.api.scheduler.Scheduler;
|
import com.velocitypowered.api.scheduler.Scheduler;
|
||||||
import com.velocitypowered.api.scheduler.TaskStatus;
|
import com.velocitypowered.api.scheduler.TaskStatus;
|
||||||
|
import com.velocitypowered.proxy.plugin.loader.VelocityPluginContainer;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.ScheduledExecutorService;
|
import java.util.concurrent.ScheduledExecutorService;
|
||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.SynchronousQueue;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
import org.jetbrains.annotations.VisibleForTesting;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Velocity "scheduler", which is actually a thin wrapper around
|
* The Velocity "scheduler", which is actually a thin wrapper around
|
||||||
* {@link ScheduledExecutorService} and a dynamically-sized {@link ExecutorService}.
|
* {@link ScheduledExecutorService} and a dynamically-sized {@link ExecutorService}.
|
||||||
* Many plugins are accustomed to the Bukkit Scheduler model although it is not relevant
|
* Many plugins are accustomed to the Bukkit Scheduler model, although it is not relevant
|
||||||
* in a proxy context.
|
* in a proxy context.
|
||||||
*/
|
*/
|
||||||
public class VelocityScheduler implements Scheduler {
|
public class VelocityScheduler implements Scheduler {
|
||||||
|
|
||||||
private static final int MAX_SCHEDULER_POOLED_THREAD_CAP = 200;
|
|
||||||
|
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
private final ExecutorService taskService;
|
|
||||||
private final ScheduledExecutorService timerExecutionService;
|
private final ScheduledExecutorService timerExecutionService;
|
||||||
private final Multimap<Object, ScheduledTask> tasksByPlugin = Multimaps.synchronizedMultimap(
|
private final Multimap<Object, ScheduledTask> tasksByPlugin = Multimaps.synchronizedMultimap(
|
||||||
Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new));
|
Multimaps.newSetMultimap(new IdentityHashMap<>(), HashSet::new));
|
||||||
@ -68,10 +68,6 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
*/
|
*/
|
||||||
public VelocityScheduler(PluginManager pluginManager) {
|
public VelocityScheduler(PluginManager pluginManager) {
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
this.taskService = new ThreadPoolExecutor(1, MAX_SCHEDULER_POOLED_THREAD_CAP,
|
|
||||||
60L, TimeUnit.SECONDS, new SynchronousQueue<>(),
|
|
||||||
new ThreadFactoryBuilder().setDaemon(true)
|
|
||||||
.setNameFormat("Velocity Task Scheduler - #%d").build());
|
|
||||||
this.timerExecutionService = Executors
|
this.timerExecutionService = Executors
|
||||||
.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setDaemon(true)
|
.newSingleThreadScheduledExecutor(new ThreadFactoryBuilder().setDaemon(true)
|
||||||
.setNameFormat("Velocity Task Scheduler Timer").build());
|
.setNameFormat("Velocity Task Scheduler Timer").build());
|
||||||
@ -81,16 +77,18 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
public TaskBuilder buildTask(Object plugin, Runnable runnable) {
|
public TaskBuilder buildTask(Object plugin, Runnable runnable) {
|
||||||
checkNotNull(plugin, "plugin");
|
checkNotNull(plugin, "plugin");
|
||||||
checkNotNull(runnable, "runnable");
|
checkNotNull(runnable, "runnable");
|
||||||
checkArgument(pluginManager.fromInstance(plugin).isPresent(), "plugin is not registered");
|
final Optional<PluginContainer> container = pluginManager.fromInstance(plugin);
|
||||||
return new TaskBuilderImpl(plugin, runnable, null);
|
checkArgument(container.isPresent(), "plugin is not registered");
|
||||||
|
return new TaskBuilderImpl(container.get(), runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TaskBuilder buildTask(Object plugin, Consumer<ScheduledTask> consumer) {
|
public TaskBuilder buildTask(Object plugin, Consumer<ScheduledTask> consumer) {
|
||||||
checkNotNull(plugin, "plugin");
|
checkNotNull(plugin, "plugin");
|
||||||
checkNotNull(consumer, "consumer");
|
checkNotNull(consumer, "consumer");
|
||||||
checkArgument(pluginManager.fromInstance(plugin).isPresent(), "plugin is not registered");
|
final Optional<PluginContainer> container = pluginManager.fromInstance(plugin);
|
||||||
return new TaskBuilderImpl(plugin, null, consumer);
|
checkArgument(container.isPresent(), "plugin is not registered");
|
||||||
|
return new TaskBuilderImpl(container.get(), consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -118,22 +116,55 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
task.cancel();
|
task.cancel();
|
||||||
}
|
}
|
||||||
timerExecutionService.shutdown();
|
timerExecutionService.shutdown();
|
||||||
taskService.shutdown();
|
for (final PluginContainer container : this.pluginManager.getPlugins()) {
|
||||||
return taskService.awaitTermination(10, TimeUnit.SECONDS);
|
if (container instanceof VelocityPluginContainer) {
|
||||||
|
(container).getExecutorService().shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean allShutdown = true;
|
||||||
|
for (final PluginContainer container : this.pluginManager.getPlugins()) {
|
||||||
|
if (!(container instanceof VelocityPluginContainer)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final String id = container.getDescription().getId();
|
||||||
|
final ExecutorService service = (container).getExecutorService();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!service.awaitTermination(10, TimeUnit.SECONDS)) {
|
||||||
|
service.shutdownNow();
|
||||||
|
Log.logger.warn("Executor for plugin {} did not shut down within 10 seconds. "
|
||||||
|
+ "Continuing with shutdown...", id);
|
||||||
|
allShutdown = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (final InterruptedException e) {
|
||||||
|
Log.logger.warn("Executor for plugin {} did not shut down within 10 seconds. "
|
||||||
|
+ "Continuing with shutdown...", id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return allShutdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TaskBuilderImpl implements TaskBuilder {
|
private class TaskBuilderImpl implements TaskBuilder {
|
||||||
|
|
||||||
private final Object plugin;
|
private final PluginContainer container;
|
||||||
private final Runnable runnable;
|
private final Runnable runnable;
|
||||||
private final Consumer<ScheduledTask> consumer;
|
private final Consumer<ScheduledTask> consumer;
|
||||||
private long delay; // ms
|
private long delay; // ms
|
||||||
private long repeat; // ms
|
private long repeat; // ms
|
||||||
|
|
||||||
private TaskBuilderImpl(Object plugin, Runnable runnable, Consumer<ScheduledTask> consumer) {
|
private TaskBuilderImpl(PluginContainer container, Consumer<ScheduledTask> consumer) {
|
||||||
this.plugin = plugin;
|
this.container = container;
|
||||||
this.runnable = runnable;
|
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
|
this.runnable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TaskBuilderImpl(PluginContainer container, Runnable runnable) {
|
||||||
|
this.container = container;
|
||||||
|
this.consumer = null;
|
||||||
|
this.runnable = runnable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -162,16 +193,17 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ScheduledTask schedule() {
|
public ScheduledTask schedule() {
|
||||||
VelocityTask task = new VelocityTask(plugin, runnable, consumer, delay, repeat);
|
VelocityTask task = new VelocityTask(container, runnable, consumer, delay, repeat);
|
||||||
tasksByPlugin.put(plugin, task);
|
tasksByPlugin.put(container.getInstance().get(), task);
|
||||||
task.schedule();
|
task.schedule();
|
||||||
return task;
|
return task;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class VelocityTask implements Runnable, ScheduledTask {
|
@VisibleForTesting
|
||||||
|
class VelocityTask implements Runnable, ScheduledTask {
|
||||||
|
|
||||||
private final Object plugin;
|
private final PluginContainer container;
|
||||||
private final Runnable runnable;
|
private final Runnable runnable;
|
||||||
private final Consumer<ScheduledTask> consumer;
|
private final Consumer<ScheduledTask> consumer;
|
||||||
private final long delay;
|
private final long delay;
|
||||||
@ -179,9 +211,9 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
private @Nullable ScheduledFuture<?> future;
|
private @Nullable ScheduledFuture<?> future;
|
||||||
private volatile @Nullable Thread currentTaskThread;
|
private volatile @Nullable Thread currentTaskThread;
|
||||||
|
|
||||||
private VelocityTask(Object plugin, Runnable runnable, Consumer<ScheduledTask> consumer,
|
private VelocityTask(PluginContainer container, Runnable runnable,
|
||||||
long delay, long repeat) {
|
Consumer<ScheduledTask> consumer, long delay, long repeat) {
|
||||||
this.plugin = plugin;
|
this.container = container;
|
||||||
this.runnable = runnable;
|
this.runnable = runnable;
|
||||||
this.consumer = consumer;
|
this.consumer = consumer;
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
@ -199,7 +231,8 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object plugin() {
|
public Object plugin() {
|
||||||
return plugin;
|
//noinspection OptionalGetWithoutIsPresent
|
||||||
|
return container.getInstance().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -235,7 +268,7 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
taskService.execute(() -> {
|
container.getExecutorService().execute(() -> {
|
||||||
currentTaskThread = Thread.currentThread();
|
currentTaskThread = Thread.currentThread();
|
||||||
try {
|
try {
|
||||||
if (runnable != null) {
|
if (runnable != null) {
|
||||||
@ -248,11 +281,10 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
if (e instanceof InterruptedException) {
|
if (e instanceof InterruptedException) {
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
} else {
|
} else {
|
||||||
String friendlyPluginName = pluginManager.fromInstance(plugin)
|
String friendlyPluginName = container.getDescription().getName()
|
||||||
.map(container -> container.getDescription().getName()
|
.orElse(container.getDescription().getId());
|
||||||
.orElse(container.getDescription().getId()))
|
Object unit = consumer == null ? runnable : consumer;
|
||||||
.orElse("UNKNOWN");
|
Log.logger.error("Exception in task {} by plugin {}", unit, friendlyPluginName,
|
||||||
Log.logger.error("Exception in task {} by plugin {}", runnable, friendlyPluginName,
|
|
||||||
e);
|
e);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -265,7 +297,17 @@ public class VelocityScheduler implements Scheduler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onFinish() {
|
private void onFinish() {
|
||||||
tasksByPlugin.remove(plugin, this);
|
tasksByPlugin.remove(plugin(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void awaitCompletion() {
|
||||||
|
try {
|
||||||
|
future.get();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} catch (ExecutionException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
|
|
||||||
import com.velocitypowered.api.scheduler.ScheduledTask;
|
import com.velocitypowered.api.scheduler.ScheduledTask;
|
||||||
import com.velocitypowered.api.scheduler.TaskStatus;
|
import com.velocitypowered.api.scheduler.TaskStatus;
|
||||||
|
import com.velocitypowered.proxy.scheduler.VelocityScheduler.VelocityTask;
|
||||||
import com.velocitypowered.proxy.testutil.FakePluginManager;
|
import com.velocitypowered.proxy.testutil.FakePluginManager;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
@ -39,6 +40,7 @@ class VelocitySchedulerTest {
|
|||||||
ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown)
|
ScheduledTask task = scheduler.buildTask(FakePluginManager.PLUGIN_A, latch::countDown)
|
||||||
.schedule();
|
.schedule();
|
||||||
latch.await();
|
latch.await();
|
||||||
|
((VelocityTask) task).awaitCompletion();
|
||||||
assertEquals(TaskStatus.FINISHED, task.status());
|
assertEquals(TaskStatus.FINISHED, task.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,7 +52,6 @@ class VelocitySchedulerTest {
|
|||||||
.delay(100, TimeUnit.SECONDS)
|
.delay(100, TimeUnit.SECONDS)
|
||||||
.schedule();
|
.schedule();
|
||||||
task.cancel();
|
task.cancel();
|
||||||
Thread.sleep(200);
|
|
||||||
assertEquals(3, i.get());
|
assertEquals(3, i.get());
|
||||||
assertEquals(TaskStatus.CANCELLED, task.status());
|
assertEquals(TaskStatus.CANCELLED, task.status());
|
||||||
}
|
}
|
||||||
@ -70,23 +71,26 @@ class VelocitySchedulerTest {
|
|||||||
@Test
|
@Test
|
||||||
void obtainTasksFromPlugin() throws Exception {
|
void obtainTasksFromPlugin() throws Exception {
|
||||||
VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager());
|
VelocityScheduler scheduler = new VelocityScheduler(new FakePluginManager());
|
||||||
AtomicInteger i = new AtomicInteger(0);
|
CountDownLatch runningLatch = new CountDownLatch(1);
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch endingLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
scheduler.buildTask(FakePluginManager.PLUGIN_A, task -> {
|
scheduler.buildTask(FakePluginManager.PLUGIN_A, task -> {
|
||||||
if (i.getAndIncrement() >= 1) {
|
runningLatch.countDown();
|
||||||
task.cancel();
|
task.cancel();
|
||||||
latch.countDown();
|
try {
|
||||||
|
endingLatch.await();
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}).delay(50, TimeUnit.MILLISECONDS)
|
}).delay(50, TimeUnit.MILLISECONDS)
|
||||||
.repeat(Duration.ofMillis(5))
|
.repeat(Duration.ofMillis(5))
|
||||||
.schedule();
|
.schedule();
|
||||||
|
|
||||||
|
runningLatch.await();
|
||||||
|
|
||||||
assertEquals(scheduler.tasksByPlugin(FakePluginManager.PLUGIN_A).size(), 1);
|
assertEquals(scheduler.tasksByPlugin(FakePluginManager.PLUGIN_A).size(), 1);
|
||||||
|
|
||||||
latch.await();
|
endingLatch.countDown();
|
||||||
|
|
||||||
assertEquals(scheduler.tasksByPlugin(FakePluginManager.PLUGIN_A).size(), 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -24,6 +24,8 @@ import com.velocitypowered.api.plugin.PluginManager;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -79,10 +81,12 @@ public class FakePluginManager implements PluginManager {
|
|||||||
|
|
||||||
private final String id;
|
private final String id;
|
||||||
private final Object instance;
|
private final Object instance;
|
||||||
|
private final ExecutorService service;
|
||||||
|
|
||||||
private FakePluginContainer(String id, Object instance) {
|
private FakePluginContainer(String id, Object instance) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
|
this.service = ForkJoinPool.commonPool();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -94,5 +98,10 @@ public class FakePluginManager implements PluginManager {
|
|||||||
public Optional<?> getInstance() {
|
public Optional<?> getInstance() {
|
||||||
return Optional.of(instance);
|
return Optional.of(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ExecutorService getExecutorService() {
|
||||||
|
return service;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Laden…
In neuem Issue referenzieren
Einen Benutzer sperren