Skip to content

Commit 4a70e41

Browse files
committed
fix(folia): route async tasks through platform schedulers
1 parent 4d4fb25 commit 4a70e41

13 files changed

Lines changed: 176 additions & 61 deletions

File tree

authme-core/src/main/java/fr/xephi/authme/AuthMe.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ public void onEnable() {
166166

167167
// Schedule clean up task
168168
CleanupTask cleanupTask = injector.getSingleton(CleanupTask.class);
169-
cleanupTask.runTaskTimerAsynchronously(this, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
169+
injector.getSingleton(BukkitService.class)
170+
.runTaskTimerAsynchronously(cleanupTask, CLEANUP_INTERVAL, CLEANUP_INTERVAL);
170171

171172
// Do a backup on start
172173
backupService.doBackup(BackupService.BackupCause.START);

authme-core/src/main/java/fr/xephi/authme/initialization/OnStartupTasks.java

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
import org.bstats.charts.SimplePie;
1919
import org.bukkit.Bukkit;
2020
import org.bukkit.entity.Player;
21-
import org.bukkit.scheduler.BukkitRunnable;
2221

2322
import javax.inject.Inject;
2423
import java.util.List;
@@ -98,19 +97,16 @@ public void scheduleRecallEmailTask() {
9897
if (!settings.getProperty(RECALL_PLAYERS)) {
9998
return;
10099
}
101-
bukkitService.runTaskTimerAsynchronously(new BukkitRunnable() {
102-
@Override
103-
public void run() {
104-
List<String> loggedPlayersWithEmptyMail = dataSource.getLoggedPlayersWithEmptyMail();
105-
bukkitService.runTask(() -> {
106-
for (String playerWithoutMail : loggedPlayersWithEmptyMail) {
107-
Player player = bukkitService.getPlayerExact(playerWithoutMail);
108-
if (player != null) {
109-
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
110-
}
100+
bukkitService.runTaskTimerAsynchronously(() -> {
101+
List<String> loggedPlayersWithEmptyMail = dataSource.getLoggedPlayersWithEmptyMail();
102+
bukkitService.runTask(() -> {
103+
for (String playerWithoutMail : loggedPlayersWithEmptyMail) {
104+
Player player = bukkitService.getPlayerExact(playerWithoutMail);
105+
if (player != null) {
106+
messages.send(player, MessageKey.ADD_EMAIL_MESSAGE);
111107
}
112-
});
113-
}
108+
}
109+
});
114110
}, 1, (long) TICKS_PER_MINUTE * settings.getProperty(EmailSettings.DELAY_RECALL));
115111
}
116112
}

authme-core/src/main/java/fr/xephi/authme/platform/AbstractSpigotPlatformAdapter.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ public void run() {
5858
return wrapTask(bukkitRunnable.runTaskTimer(plugin, delay, period));
5959
}
6060

61+
@Override
62+
public CancellableTask runAsyncTask(AuthMe plugin, Runnable task) {
63+
return wrapTask(Bukkit.getScheduler().runTaskAsynchronously(plugin, task));
64+
}
65+
66+
@Override
67+
public CancellableTask runAsyncTaskTimer(AuthMe plugin, Runnable task, long delay, long period) {
68+
BukkitRunnable bukkitRunnable = new BukkitRunnable() {
69+
@Override
70+
public void run() {
71+
task.run();
72+
}
73+
};
74+
return wrapTask(bukkitRunnable.runTaskTimerAsynchronously(plugin, delay, period));
75+
}
76+
6177
@Override
6278
public void runOnGlobalThread(AuthMe plugin, Runnable task) {
6379
Bukkit.getScheduler().runTask(plugin, task);

authme-core/src/main/java/fr/xephi/authme/platform/SchedulingAdapter.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,26 @@ public interface SchedulingAdapter {
5454
*/
5555
CancellableTask runAtFixedRateOnEntityThread(AuthMe plugin, Entity entity, Runnable task, long delay, long period);
5656

57+
/**
58+
* Runs a one-off background task that is not tied to a global or entity-owned thread.
59+
*
60+
* @param plugin the plugin scheduling the task
61+
* @param task the task to run
62+
* @return the scheduled task handle
63+
*/
64+
CancellableTask runAsyncTask(AuthMe plugin, Runnable task);
65+
66+
/**
67+
* Runs a repeating background task that is not tied to a global or entity-owned thread.
68+
*
69+
* @param plugin the plugin scheduling the task
70+
* @param task the task to run
71+
* @param delay the initial delay in ticks
72+
* @param period the repeat period in ticks
73+
* @return the scheduled task handle
74+
*/
75+
CancellableTask runAsyncTaskTimer(AuthMe plugin, Runnable task, long delay, long period);
76+
5777
/**
5878
* Runs a task on the platform's global execution context.
5979
*

authme-core/src/main/java/fr/xephi/authme/service/BukkitService.java

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -216,12 +216,12 @@ public void runTaskOptionallyAsync(Runnable task) {
216216
* Returns a task that will run asynchronously.
217217
*
218218
* @param task the task to be run
219-
* @return a BukkitTask that contains the id number
219+
* @return the scheduled task handle
220220
* @throws IllegalArgumentException if plugin is null
221221
* @throws IllegalArgumentException if task is null
222222
*/
223-
public BukkitTask runTaskAsynchronously(Runnable task) {
224-
return Bukkit.getScheduler().runTaskAsynchronously(authMe, task);
223+
public CancellableTask runTaskAsynchronously(Runnable task) {
224+
return schedulingAdapter.runAsyncTask(authMe, task);
225225
}
226226

227227
/**
@@ -235,12 +235,11 @@ public BukkitTask runTaskAsynchronously(Runnable task) {
235235
* @param delay the ticks to wait before running the task for the first
236236
* time
237237
* @param period the ticks to wait between runs
238-
* @return a BukkitTask that contains the id number
238+
* @return the scheduled task handle
239239
* @throws IllegalArgumentException if task is null
240-
* @throws IllegalStateException if this was already scheduled
241240
*/
242-
public BukkitTask runTaskTimerAsynchronously(BukkitRunnable task, long delay, long period) {
243-
return task.runTaskTimerAsynchronously(authMe, delay, period);
241+
public CancellableTask runTaskTimerAsynchronously(Runnable task, long delay, long period) {
242+
return schedulingAdapter.runAsyncTaskTimer(authMe, task, delay, period);
244243
}
245244

246245
/**

authme-core/src/main/java/fr/xephi/authme/task/CleanupTask.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22

33
import ch.jalu.injector.factory.SingletonStore;
44
import fr.xephi.authme.initialization.HasCleanup;
5-
import org.bukkit.scheduler.BukkitRunnable;
65

76
import javax.inject.Inject;
87

98
/**
109
* Task run periodically to invoke the cleanup task on services.
1110
*/
12-
public class CleanupTask extends BukkitRunnable {
11+
public class CleanupTask implements Runnable {
1312

1413
@Inject
1514
private SingletonStore<HasCleanup> hasCleanupStore;

authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeService.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import fr.xephi.authme.datasource.DataSource;
55
import fr.xephi.authme.output.ConsoleLoggerFactory;
66
import fr.xephi.authme.permission.PermissionsManager;
7+
import fr.xephi.authme.service.CancellableTask;
78
import fr.xephi.authme.service.BukkitService;
89
import fr.xephi.authme.settings.Settings;
910
import fr.xephi.authme.settings.properties.PurgeSettings;
@@ -99,7 +100,8 @@ public void purgePlayers(CommandSender sender, Set<String> names, OfflinePlayer[
99100

100101
isPurging = true;
101102
PurgeTask purgeTask = new PurgeTask(this, permissionsManager, sender, names, players);
102-
bukkitService.runTaskTimerAsynchronously(purgeTask, 0, 1);
103+
CancellableTask taskHandle = bukkitService.runTaskTimerAsynchronously(purgeTask, 0, 1);
104+
purgeTask.setTaskHandle(taskHandle);
103105
}
104106

105107
/**

authme-core/src/main/java/fr/xephi/authme/task/purge/PurgeTask.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
import org.bukkit.OfflinePlayer;
1010
import org.bukkit.command.CommandSender;
1111
import org.bukkit.entity.Player;
12-
import org.bukkit.scheduler.BukkitRunnable;
12+
import fr.xephi.authme.service.CancellableTask;
1313

1414
import java.util.HashSet;
1515
import java.util.Locale;
1616
import java.util.Set;
1717
import java.util.UUID;
1818

19-
class PurgeTask extends BukkitRunnable {
19+
class PurgeTask implements Runnable {
2020

2121
//how many players we should check for each tick
2222
private static final int INTERVAL_CHECK = 5;
@@ -31,6 +31,8 @@ class PurgeTask extends BukkitRunnable {
3131
private final int totalPurgeCount;
3232

3333
private int currentPage = 0;
34+
private CancellableTask taskHandle;
35+
private boolean cancellationRequested;
3436

3537
/**
3638
* Constructor.
@@ -108,7 +110,7 @@ public void run() {
108110
}
109111

110112
private void finish() {
111-
cancel();
113+
cancelTask();
112114

113115
// Show a status message
114116
sendMessage(ChatColor.GREEN + "[AuthMe] Database has been purged successfully");
@@ -117,6 +119,16 @@ private void finish() {
117119
purgeService.setPurging(false);
118120
}
119121

122+
void setTaskHandle(CancellableTask taskHandle) {
123+
synchronized (this) {
124+
if (cancellationRequested) {
125+
taskHandle.cancel();
126+
} else {
127+
this.taskHandle = taskHandle;
128+
}
129+
}
130+
}
131+
120132
private void sendMessage(String message) {
121133
if (sender == null) {
122134
Bukkit.getConsoleSender().sendMessage(message);
@@ -127,4 +139,14 @@ private void sendMessage(String message) {
127139
}
128140
}
129141
}
142+
143+
private void cancelTask() {
144+
synchronized (this) {
145+
if (taskHandle != null) {
146+
taskHandle.cancel();
147+
} else {
148+
cancellationRequested = true;
149+
}
150+
}
151+
}
130152
}

authme-core/src/test/java/fr/xephi/authme/service/BukkitServiceTest.java

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ public void shouldRunTaskInAsync() {
217217
// given
218218
Runnable task = mock(Runnable.class);
219219
BukkitService spy = Mockito.spy(bukkitService);
220-
doReturn(null).when(spy).runTaskAsynchronously(task);
220+
doReturn(mock(CancellableTask.class)).when(spy).runTaskAsynchronously(task);
221221

222222
// when
223223
spy.runTaskOptionallyAsync(task);
@@ -247,35 +247,32 @@ public void shouldRunTaskDirectlyIfConfigured() {
247247
public void shouldRunTaskAsynchronously() {
248248
// given
249249
Runnable task = () -> {/* noop */};
250-
BukkitTask bukkitTask = mock(BukkitTask.class);
251-
given(scheduler.runTaskAsynchronously(authMe, task)).willReturn(bukkitTask);
250+
CancellableTask cancellableTask = mock(CancellableTask.class);
251+
given(schedulingAdapter.runAsyncTask(authMe, task)).willReturn(cancellableTask);
252252

253253
// when
254-
BukkitTask resultingTask = bukkitService.runTaskAsynchronously(task);
254+
CancellableTask resultingTask = bukkitService.runTaskAsynchronously(task);
255255

256256
// then
257-
assertThat(resultingTask, equalTo(bukkitTask));
258-
verify(scheduler, only()).runTaskAsynchronously(authMe, task);
257+
assertThat(resultingTask, equalTo(cancellableTask));
258+
verify(schedulingAdapter, only()).runAsyncTask(authMe, task);
259259
}
260260

261261
@Test
262262
public void shouldRunTaskTimerAsynchronously() {
263263
// given
264-
BukkitRunnable task = new BukkitRunnable() {
265-
@Override
266-
public void run() {
267-
}
268-
};
264+
Runnable task = mock(Runnable.class);
269265
long delay = 20L;
270266
long period = 4000L;
271-
BukkitTask bukkitTask = mock(BukkitTask.class);
272-
given(task.runTaskTimerAsynchronously(authMe, delay, period)).willReturn(bukkitTask);
267+
CancellableTask cancellableTask = mock(CancellableTask.class);
268+
given(schedulingAdapter.runAsyncTaskTimer(authMe, task, delay, period)).willReturn(cancellableTask);
273269

274270
// when
275-
BukkitTask resultingTask = bukkitService.runTaskTimerAsynchronously(task, delay, period);
271+
CancellableTask resultingTask = bukkitService.runTaskTimerAsynchronously(task, delay, period);
276272

277273
// then
278-
assertThat(resultingTask, equalTo(bukkitTask));
274+
assertThat(resultingTask, equalTo(cancellableTask));
275+
verify(schedulingAdapter, only()).runAsyncTaskTimer(authMe, task, delay, period);
279276
}
280277

281278
@Test

authme-core/src/test/java/fr/xephi/authme/task/purge/PurgeServiceTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public class PurgeServiceTest {
6868
@Captor
6969
private ArgumentCaptor<Long> longCaptor;
7070
@Captor
71-
private ArgumentCaptor<PurgeTask> purgeTaskCaptor;
71+
private ArgumentCaptor<Runnable> purgeTaskCaptor;
7272

7373
@BeforeAll
7474
public static void initLogger() {
@@ -196,7 +196,7 @@ private void assertCorrectPurgeTimestamp(long timestamp, int configuredDays) {
196196

197197
private void verifyScheduledPurgeTask(UUID senderUuid, Set<String> names) {
198198
verify(bukkitService).runTaskTimerAsynchronously(purgeTaskCaptor.capture(), eq(0L), eq(1L));
199-
PurgeTask task = purgeTaskCaptor.getValue();
199+
PurgeTask task = (PurgeTask) purgeTaskCaptor.getValue();
200200

201201
Object senderInTask = ReflectionTestUtils.getFieldValue(PurgeTask.class, task, "sender");
202202
Set<String> namesInTask = ReflectionTestUtils.getFieldValue(PurgeTask.class, task, "toPurge");

0 commit comments

Comments
 (0)