PaperMC / Paper

The most widely used, high performance Minecraft server that aims to fix gameplay and mechanics inconsistencies
https://papermc.io/
Other
9.39k stars 2.2k forks source link

Chat chain breaks when chat thread pool max size = 1 #10813

Open emilyy-dev opened 1 month ago

emilyy-dev commented 1 month ago

Expected behavior

not get kicked when chatting

Observed/Actual behavior

chat chain is broken and players get kicked

Steps/models to reproduce

  1. Set misc.chat-threads.chat-executor-max-size to 1 in config/paper-global.yml
  2. Start the server, join and try to chat. The first message sent will trigger an exception and a stack trace will be shown from the chat thread pool, following messages will kick the player

Plugin and Datapack List

N/A

Paper version

Paper version 1.20.6-115-master@9d6f2cc

Other

[23:23:33] [User Authenticator #0/INFO]: UUID of player emilyy_dev is 33ae6cd1-f352-45b1-8a98-39ede5284d5e
[23:23:33] [Server thread/INFO]: emilyy_dev joined the game
[23:23:33] [Server thread/INFO]: emilyy_dev[/127.0.0.1:50710] logged in with entity id 240 at ([world]-98.96601467549034, 100.0, 705.1372853987785)
[23:23:37] [Async Chat Thread - #0/ERROR]: Chain link failed, continuing to next one
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.CompletableFuture$UniAccept@7279a2b7 rejected from java.util.concurrent.ThreadPoolExecutor@3bfe979d[Running, pool size = 1, active threads = 1, queued tasks = 0, completed tasks = 2]
    at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2081) ~[?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:841) ~[?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1376) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture$UniCompletion.claim(CompletableFuture.java:572) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture$UniAccept.tryFire(CompletableFuture.java:714) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[?:?]
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1773) ~[?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[?:?]
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[?:?]
    at java.base/java.lang.Thread.run(Thread.java:1583) ~[?:?]
[23:23:39] [Server thread/INFO]: emilyy_dev lost connection: Server closed
[23:23:39] [Server thread/INFO]: emilyy_dev left the game
emilyy-dev commented 1 month ago

The exception comes from the FutureChain, the new future that is appended is already running in the chat thread, and the thenAcceptAsync tries to schedule a new task on the same executor that is currently busy. The chat executor is a ThreadPoolExecutor using a SynchronousQueue, that means it can only schedule tasks if there is a thread waiting for one, and given the max pool size is 1 and that thread is currently busy, it rejects the task. Not entirely sure how this busts the chain