This commit is contained in:
Sotr
2019-03-19 05:00:23 +08:00
parent 9ccbd91d78
commit dbfd5f4f2d
14 changed files with 346 additions and 20 deletions

View File

@@ -0,0 +1,13 @@
package co.aikar.timings;
public class ThreadAssertion {
private static boolean mainThread;
public static boolean isMainThread() {
return mainThread;
}
static boolean setMainThread(boolean is) {
return mainThread = is;
}
}

View File

@@ -0,0 +1,78 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
/**
* Provides an ability to time sections of code within the Minecraft Server
*/
public interface Timing extends AutoCloseable {
/**
* Starts timing the execution until {@link #stopTiming()} is called.
*
* @return Timing
*/
Timing startTiming();
default Timing startTiming(boolean assertThread) { return startTiming(); }; // Akarin
/**
* <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
*
* Will automatically be called when this Timing is used with try-with-resources
*/
void stopTiming();
/**
* Starts timing the execution until {@link #stopTiming()} is called.
*
* But only if we are on the primary thread.
*
* @return Timing
*/
Timing startTimingIfSync();
default Timing startTimingIfSync(boolean assertThread) { return startTimingIfSync(); }; // Akarin
/**
* <p>Stops timing and records the data. Propagates the data up to group handlers.</p>
*
* <p>Will automatically be called when this Timing is used with try-with-resources</p>
*
* But only if we are on the primary thread.
*/
void stopTimingIfSync();
/**
* Stops timing and disregards current timing data.
*/
void abort();
/**
* Used internally to get the actual backing Handler in the case of delegated Handlers
*
* @return TimingHandler
*/
TimingHandler getTimingHandler();
@Override
void close();
}

View File

@@ -0,0 +1,222 @@
/*
* This file is licensed under the MIT License (MIT).
*
* Copyright (c) 2014 Daniel Ennis <http://aikar.co>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package co.aikar.timings;
import co.aikar.util.LoadingIntMap;
import io.akarin.server.core.AkarinGlobalConfig;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import org.bukkit.Bukkit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
class TimingHandler implements Timing {
private static AtomicInteger idPool = new AtomicInteger(1);
final int id = idPool.getAndIncrement();
final String name;
private final boolean verbose;
private final Int2ObjectOpenHashMap<TimingData> children = new LoadingIntMap<>(TimingData::new);
final TimingData record;
private final TimingHandler groupHandler;
private long start = 0;
private int timingDepth = 0;
private boolean added;
private boolean timed;
private boolean enabled;
private TimingHandler parent;
TimingHandler(TimingIdentifier id) {
if (id.name.startsWith("##")) {
verbose = true;
this.name = id.name.substring(3);
} else {
this.name = id.name;
verbose = false;
}
this.record = new TimingData(this.id);
this.groupHandler = id.groupHandler;
TimingIdentifier.getGroup(id.group).handlers.add(this);
checkEnabled();
}
final void checkEnabled() {
enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled);
}
void processTick(boolean violated) {
if (timingDepth != 0 || record.getCurTickCount() == 0) {
timingDepth = 0;
start = 0;
return;
}
record.processTick(violated);
for (TimingData handler : children.values()) {
handler.processTick(violated);
}
}
@Override
// Akarin start
public Timing startTimingIfSync() {
return startTiming(false);
}
@Override
// Akarin end
public Timing startTimingIfSync(boolean assertThread) {
startTiming(assertThread);
return this;
}
@Override
public void stopTimingIfSync() {
stopTiming();
}
// Akarin start
@Override
public Timing startTiming() {
return startTiming(false);
}
@Override
public Timing startTiming(boolean assertThread) {
if (enabled && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& ++timingDepth == 1*/) {
if (AkarinGlobalConfig.lazyThreadAssertion && assertThread) ThreadAssertion.setMainThread(true);
if (++timingDepth != 1) return this;
// Akarin end
start = System.nanoTime();
parent = TimingsManager.CURRENT;
TimingsManager.CURRENT = this;
}
return this;
}
@Override
public void stopTiming() {
// Akarin start
if (enabled && timingDepth > 0 && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& --timingDepth == 0 && start != 0*/) {
if (AkarinGlobalConfig.lazyThreadAssertion) ThreadAssertion.setMainThread(false);
if (--timingDepth != 0 || start == 0) return;
// Akarin end
addDiff(System.nanoTime() - start);
start = 0;
}
}
@Override
public void abort() {
if (enabled && timingDepth > 0) {
start = 0;
}
}
void addDiff(long diff) {
if (TimingsManager.CURRENT == this) {
TimingsManager.CURRENT = parent;
if (parent != null) {
parent.children.get(id).add(diff);
}
}
record.add(diff);
if (!added) {
added = true;
timed = true;
TimingsManager.HANDLERS.add(this);
}
if (groupHandler != null) {
groupHandler.addDiff(diff);
groupHandler.children.get(id).add(diff);
}
}
/**
* Reset this timer, setting all values to zero.
*
* @param full
*/
void reset(boolean full) {
record.reset();
if (full) {
timed = false;
}
start = 0;
timingDepth = 0;
added = false;
children.clear();
checkEnabled();
}
@Override
public TimingHandler getTimingHandler() {
return this;
}
@Override
public boolean equals(Object o) {
return (this == o);
}
@Override
public int hashCode() {
return id;
}
/**
* This is simply for the Closeable interface so it can be used with
* try-with-resources ()
*/
@Override
public void close() {
stopTimingIfSync();
}
public boolean isSpecial() {
return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK;
}
boolean isTimed() {
return timed;
}
public boolean isEnabled() {
return enabled;
}
TimingData[] cloneChildren() {
final TimingData[] clonedChildren = new TimingData[children.size()];
int i = 0;
for (TimingData child : children.values()) {
clonedChildren[i++] = child.clone();
}
return clonedChildren;
}
}

View File

@@ -10,15 +10,12 @@ import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bukkit.Bukkit;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
@SuppressWarnings({"UnusedIsStillUsed", "unused"})
public class AkarinGlobalConfig {
public final static Logger LOGGER = LogManager.getLogger("Akarin");
private static File CONFIG_FILE;
private static final String HEADER = "This is the global configuration file for Akarin.\n"
+ "Some options may impact gameplay, so use with caution,\n"
@@ -36,7 +33,8 @@ public class AkarinGlobalConfig {
config.load(CONFIG_FILE);
} catch (IOException ex) {
} catch (InvalidConfigurationException ex) {
LOGGER.error("Could not load akarin.yml, please correct your syntax errors", ex);
Bukkit.getLogger().severe("Could not load akarin.yml, please correct your syntax errors");
ex.printStackTrace();
throw Throwables.propagate(ex);
}
config.options().header(HEADER);
@@ -57,7 +55,8 @@ public class AkarinGlobalConfig {
} catch (InvocationTargetException ex) {
throw Throwables.propagate(ex.getCause());
} catch (Exception ex) {
LOGGER.error("Error invoking " + method, ex);
Bukkit.getLogger().severe("Error invoking " + method);
ex.printStackTrace();
}
}
}
@@ -66,7 +65,8 @@ public class AkarinGlobalConfig {
try {
config.save(CONFIG_FILE);
} catch (IOException ex) {
LOGGER.error("Could not save " + CONFIG_FILE, ex);
Bukkit.getLogger().severe("Could not save " + CONFIG_FILE);
ex.printStackTrace();
}
}
@@ -165,4 +165,9 @@ public class AkarinGlobalConfig {
private static void fixPhysicsEventBehavior() {
fixPhysicsEventBehaviour = getBoolean("alternative.fix-physics-event-behaviour", fixPhysicsEventBehaviour);
}
public static boolean lazyThreadAssertion = true;
private static void lazyThreadAssertion() {
lazyThreadAssertion = getBoolean("core.lazy-thread-assertion", lazyThreadAssertion);
}
}

View File

@@ -1,6 +1,6 @@
package io.akarin.server.core;
public enum PacketType { // unused yet
public enum PacketType {
STATUS_OUT_SERVER_INFO,
STATUS_OUT_PONG,

View File

@@ -14,8 +14,8 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
}
public Chunk put(long i, Chunk chunk) {
org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper
chunk.world.timings.syncChunkLoadPostTimer.startTiming(); // Paper
//org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper // Akarin - comment
lastChunkByPos = chunk; // Paper
// Paper start
Chunk chunk1;
@@ -81,7 +81,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap<Chunk> {
public Chunk remove(long i) {
// Paper start
org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove");
//org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove"); // Akarin - comment
Chunk chunk;
synchronized (this) {
// synchronize so any async gets are safe

View File

@@ -5,6 +5,9 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile;
import com.destroystokyo.paper.profile.PlayerProfile;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.mojang.authlib.GameProfile;
import co.aikar.timings.ThreadAssertion;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Location;
import org.bukkit.block.BlockFace;
@@ -115,7 +118,7 @@ public final class MCUtil {
* @return
*/
public static void ensureMain(String reason, Runnable run) {
if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
if (AsyncCatcher.enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { // Akarin
if (reason != null) {
new IllegalStateException("Asynchronous " + reason + "!").printStackTrace();
}
@@ -140,7 +143,7 @@ public final class MCUtil {
* @return
*/
public static <T> T ensureMain(String reason, Supplier<T> run) {
if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) {
if (AsyncCatcher.enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { // Akarin
if (reason != null) {
new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace();
}

View File

@@ -16,6 +16,7 @@ import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.datafixers.DataFixer;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
@@ -65,6 +66,7 @@ import org.bukkit.event.server.ServerLoadEvent;
// CraftBukkit end
import org.spigotmc.SlackActivityAccountant; // Spigot
import co.aikar.timings.MinecraftTimings; // Paper
import co.aikar.timings.ThreadAssertion;
public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStatistics, ICommandListener, Runnable {
@@ -1704,7 +1706,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati
}
public boolean isMainThread() {
return Thread.currentThread() == this.serverThread;
return ThreadAssertion.isMainThread() || Thread.currentThread() == this.serverThread; // Akarin
}
public int aw() {

View File

@@ -69,6 +69,7 @@ import com.destroystokyo.paper.event.player.IllegalPacketEvent; // Paper
import com.destroystokyo.paper.event.player.PlayerJumpEvent; // Paper
import co.aikar.timings.MinecraftTimings; // Paper
// CraftBukkit end
import co.aikar.timings.ThreadAssertion;
public class PlayerConnection implements PacketListenerPlayIn, ITickable {
@@ -1756,7 +1757,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
if (!async && s.startsWith("/")) {
// Paper Start
if (!org.spigotmc.AsyncCatcher.shuttingDown && !org.bukkit.Bukkit.isPrimaryThread()) {
if (!org.spigotmc.AsyncCatcher.shuttingDown && !ThreadAssertion.isMainThread() && !org.bukkit.Bukkit.isPrimaryThread()) {
final String fCommandLine = s;
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Command Dispatched Async: " + fCommandLine);
MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable());

View File

@@ -139,6 +139,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode;
import co.aikar.timings.ThreadAssertion;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
@@ -709,7 +710,7 @@ public final class CraftServer implements Server {
org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot
// Paper Start
if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) {
if (!org.spigotmc.AsyncCatcher.shuttingDown && !ThreadAssertion.isMainThread() && !Bukkit.isPrimaryThread()) { // Akarin
final CommandSender fSender = sender;
final String fCommandLine = commandLine;
Bukkit.getLogger().log(Level.SEVERE, "Command Dispatched Async: " + commandLine);
@@ -1723,7 +1724,7 @@ public final class CraftServer implements Server {
@Override
public boolean isPrimaryThread() {
return Thread.currentThread().equals(console.primaryThread);
return ThreadAssertion.isMainThread() || Thread.currentThread().equals(console.primaryThread);
}
@Override

View File

@@ -40,7 +40,7 @@ class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider<QueuedChu
queuedChunk.provider.getChunkAt(queuedChunk.x, queuedChunk.z, true, true); // Paper - actually call original if it was already loaded
return;
}
try (Timing ignored = queuedChunk.provider.world.timings.chunkIOStage2.startTimingIfSync()) { // Paper
try (Timing ignored = queuedChunk.provider.world.timings.chunkIOStage2.startTimingIfSync(true)) { // Paper // Akarin
queuedChunk.loader.loadEntities(queuedChunk.compound.getCompound("Level"), chunk);
chunk.setLastSaved(queuedChunk.provider.world.getTime());

View File

@@ -1,5 +1,6 @@
package org.spigotmc;
import co.aikar.timings.ThreadAssertion;
import net.minecraft.server.MinecraftServer;
public class AsyncCatcher
@@ -10,7 +11,7 @@ public class AsyncCatcher
public static void catchOp(String reason)
{
if ( enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread )
if ( enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread )
{
throw new IllegalStateException( "Asynchronous " + reason + "!" );
}