diff --git a/sources/src/main/java/co/aikar/timings/TimingData.java b/sources/src/main/java/co/aikar/timings/TimingData.java deleted file mode 100644 index 6178328f2..000000000 --- a/sources/src/main/java/co/aikar/timings/TimingData.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * This file is licensed under the MIT License (MIT). - * - * Copyright (c) 2014 Daniel Ennis - * - * 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 java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.LongAdder; - -import static co.aikar.util.JSONUtil.toArray; - -/** - * Akarin Changes Note - * 1) Thread safe timing (safety) - */ -/** - *

Lightweight object for tracking timing data

- * - * This is broken out to reduce memory usage - */ -class TimingData { - private final int id; - private int count = 0; - private int lagCount = 0; - private long totalTime = 0; - private long lagTotalTime = 0; - private AtomicInteger curTickCount = new AtomicInteger(); // Akarin - private LongAdder curTickTotal = new LongAdder(); // Akarin - - TimingData(int id) { - this.id = id; - } - - private TimingData(TimingData data) { - this.id = data.id; - this.totalTime = data.totalTime; - this.lagTotalTime = data.lagTotalTime; - this.count = data.count; - this.lagCount = data.lagCount; - } - - void add(long diff) { - curTickCount.incrementAndGet(); - curTickTotal.add(diff); - } - - void processTick(boolean violated) { - totalTime += curTickTotal.sum(); - count += curTickCount.get(); - if (violated) { - lagTotalTime += curTickTotal.sum(); - lagCount += curTickCount.get(); - } - curTickTotal.reset(); - curTickCount.set(0); - } - - void reset() { - count = 0; - lagCount = 0; - curTickTotal.reset(); - curTickCount.set(0); - totalTime = 0; - lagTotalTime = 0; - } - - protected TimingData clone() { - return new TimingData(this); - } - - List export() { - List list = toArray( - id, - count, - totalTime); - if (lagCount > 0) { - list.add(lagCount); - list.add(lagTotalTime); - } - return list; - } - - boolean hasData() { - return count > 0; - } - - long getTotalTime() { - return totalTime; - } - - int getCurTickCount() { - return curTickCount.get(); - } - - void setCurTickCount(int curTickCount) { - this.curTickCount.getAndSet(curTickCount); - } - - long getCurTickTotal() { - return curTickTotal.sum(); - } - - void setCurTickTotal(long curTickTotal) { - this.curTickTotal.reset(); - this.curTickTotal.add(curTickTotal); - } -} diff --git a/sources/src/main/java/co/aikar/timings/TimingHandler.java b/sources/src/main/java/co/aikar/timings/TimingHandler.java deleted file mode 100644 index ce5799278..000000000 --- a/sources/src/main/java/co/aikar/timings/TimingHandler.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * This file is licensed under the MIT License (MIT). - * - * Copyright (c) 2014 Daniel Ennis - * - * 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.api.internal.Akari; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - -import org.bukkit.Bukkit; - -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; -import java.util.logging.Level; - -/** - * Akarin Changes Note - * 1) Thread safe timing (safety) - */ -class TimingHandler implements Timing { - - private static int idPool = 1; - final int id = idPool++; - - final String name; - private final boolean verbose; - - private final Int2ObjectOpenHashMap children = new LoadingIntMap<>(TimingData::new); - - final TimingData record; - private final TimingHandler groupHandler; - - private AtomicLong start = new AtomicLong(); // Akarin - private AtomicInteger timingDepth = new AtomicInteger(); // Akarin - private volatile boolean added; // Akarin - private boolean timed; - private boolean enabled; - private volatile TimingHandler parent; // Akarin - - 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.get() != 0 || record.getCurTickCount() == 0) { - timingDepth.set(0); - start.set(0); - return; - } - - record.processTick(violated); - Akari.timingsLock.lock(); // Akarin - for (TimingData handler : children.values()) { - handler.processTick(violated); - } - Akari.timingsLock.unlock(); // Akarin - } - - @Override - public Timing startTimingIfSync() { - if (Bukkit.isPrimaryThread()) { - startTiming(); - } - return this; - } - - @Override - public void stopTimingIfSync() { - if (Bukkit.isPrimaryThread()) { - stopTiming(); - } - } - - public Timing startTiming() { - if (enabled && timingDepth.incrementAndGet() == 1) { - start.getAndSet(System.nanoTime()); - parent = TimingsManager.CURRENT; - TimingsManager.CURRENT = this; - } - return this; - } - - public void stopTiming() { - if (enabled && timingDepth.decrementAndGet() == 0 && start.get() != 0) { - if (!Bukkit.isPrimaryThread()) { - Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name); - new Throwable().printStackTrace(); - start.getAndSet(0); - return; - } - long prev = start.getAndSet(0); // Akarin - addDiff(System.nanoTime() - prev); // Akarin - } - } - - @Override - public void abort() { - if (enabled && timingDepth.get() > 0) { - start.getAndSet(0); - } - } - - void addDiff(long diff) { - if (TimingsManager.CURRENT == this) { - TimingsManager.CURRENT = parent; - if (parent != null) { - Akari.timingsLock.lock(); // Akarin - parent.children.get(id).add(diff); - Akari.timingsLock.unlock(); // Akarin - } - } - record.add(diff); - if (!added) { - added = true; - timed = true; - Akari.timingsLock.lock(); // Akarin - TimingsManager.HANDLERS.add(this); - Akari.timingsLock.unlock(); // Akarin - } - if (groupHandler != null) { - groupHandler.addDiff(diff); - Akari.timingsLock.lock(); // Akarin - groupHandler.children.get(id).add(diff); - Akari.timingsLock.unlock(); // Akarin - } - } - - /** - * Reset this timer, setting all values to zero. - * - * @param full - */ - void reset(boolean full) { - record.reset(); - if (full) { - timed = false; - } - start.set(0); - timingDepth.set(0); - added = false; - Akari.timingsLock.lock(); // Akarin - children.clear(); - Akari.timingsLock.unlock(); // Akarin - 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() { - int i = 0; - Akari.timingsLock.lock(); // Akarin - final TimingData[] clonedChildren = new TimingData[children.size()]; - for (TimingData child : children.values()) { - clonedChildren[i++] = child.clone(); - } - Akari.timingsLock.unlock(); // Akarin - return clonedChildren; - } -} diff --git a/sources/src/main/java/co/aikar/timings/TimingHistory.java b/sources/src/main/java/co/aikar/timings/TimingHistory.java deleted file mode 100644 index a5c5922de..000000000 --- a/sources/src/main/java/co/aikar/timings/TimingHistory.java +++ /dev/null @@ -1,355 +0,0 @@ -/* - * This file is licensed under the MIT License (MIT). - * - * Copyright (c) 2014 Daniel Ennis - * - * 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.timings.TimingHistory.RegionData.RegionId; -import co.aikar.util.JSONUtil; -import com.google.common.base.Function; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; -import org.bukkit.Bukkit; -import org.bukkit.Chunk; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.block.BlockState; -import org.bukkit.entity.Entity; -import org.bukkit.entity.EntityType; -import org.bukkit.entity.Player; -import co.aikar.util.LoadingMap; -import co.aikar.util.MRUMapCache; -import io.akarin.api.internal.Akari; - -import java.lang.management.ManagementFactory; -import java.util.Collection; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static co.aikar.timings.TimingsManager.FULL_SERVER_TICK; -import static co.aikar.timings.TimingsManager.MINUTE_REPORTS; -import static co.aikar.util.JSONUtil.*; - -@SuppressWarnings({"deprecation", "SuppressionAnnotation", "Convert2Lambda", "Anonymous2MethodRef"}) -public class TimingHistory { - public static long lastMinuteTime; - public static long timedTicks; - public static long playerTicks; - public static long entityTicks; - public static long tileEntityTicks; - public static long activatedEntityTicks; - private static int worldIdPool = 1; - static Map worldMap = LoadingMap.newHashMap(new Function() { - @Override - public Integer apply(String input) { - return worldIdPool++; - } - }); - private final long endTime; - private final long startTime; - private final long totalTicks; - private final long totalTime; // Represents all time spent running the server this history - private final MinuteReport[] minuteReports; - - private final TimingHistoryEntry[] entries; - final Set tileEntityTypeSet = Sets.newHashSet(); - final Set entityTypeSet = Sets.newHashSet(); - private final Map worlds; - - TimingHistory() { - this.endTime = System.currentTimeMillis() / 1000; - this.startTime = TimingsManager.historyStart / 1000; - if (timedTicks % 1200 != 0 || MINUTE_REPORTS.isEmpty()) { - this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size() + 1]); - this.minuteReports[this.minuteReports.length - 1] = new MinuteReport(); - } else { - this.minuteReports = MINUTE_REPORTS.toArray(new MinuteReport[MINUTE_REPORTS.size()]); - } - long ticks = 0; - for (MinuteReport mp : this.minuteReports) { - ticks += mp.ticksRecord.timed; - } - this.totalTicks = ticks; - this.totalTime = FULL_SERVER_TICK.record.getTotalTime(); - Akari.timingsLock.lock(); // Akarin - this.entries = new TimingHistoryEntry[TimingsManager.HANDLERS.size()]; - - int i = 0; - for (TimingHandler handler : TimingsManager.HANDLERS) { - entries[i++] = new TimingHistoryEntry(handler); - } - Akari.timingsLock.unlock(); // Akarin - - - // Information about all loaded chunks/entities - //noinspection unchecked - this.worlds = toObjectMapper(Bukkit.getWorlds(), new Function() { - @Override - public JSONPair apply(World world) { - Map regions = LoadingMap.newHashMap(RegionData.LOADER); - - for (Chunk chunk : world.getLoadedChunks()) { - RegionData data = regions.get(new RegionId(chunk.getX(), chunk.getZ())); - - for (Entity entity : chunk.getEntities()) { - if (entity == null) { - Bukkit.getLogger().warning("Null entity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ()); - continue; - } - - data.entityCounts.get(entity.getType()).increment(); - } - - for (BlockState tileEntity : chunk.getTileEntities()) { - if (tileEntity == null) { - Bukkit.getLogger().warning("Null tileentity detected in chunk at position x: " + chunk.getX() + ", z: " + chunk.getZ()); - continue; - } - - data.tileEntityCounts.get(tileEntity.getBlock().getType()).increment(); - } - } - return pair( - worldMap.get(world.getName()), - toArrayMapper(regions.values(),new Function() { - @Override - public Object apply(RegionData input) { - return toArray( - input.regionId.x, - input.regionId.z, - toObjectMapper(input.entityCounts.entrySet(), - new Function, JSONPair>() { - @Override - public JSONPair apply(Map.Entry entry) { - entityTypeSet.add(entry.getKey()); - return pair( - String.valueOf(entry.getKey().getTypeId()), - entry.getValue().count() - ); - } - } - ), - toObjectMapper(input.tileEntityCounts.entrySet(), - new Function, JSONPair>() { - @Override - public JSONPair apply(Map.Entry entry) { - tileEntityTypeSet.add(entry.getKey()); - return pair( - String.valueOf(entry.getKey().getId()), - entry.getValue().count() - ); - } - } - ) - ); - } - }) - ); - } - }); - } - static class RegionData { - final RegionId regionId; - @SuppressWarnings("Guava") - static Function LOADER = new Function() { - @Override - public RegionData apply(RegionId id) { - return new RegionData(id); - } - }; - RegionData(RegionId id) { - this.regionId = id; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - RegionData that = (RegionData) o; - - return regionId.equals(that.regionId); - - } - - @Override - public int hashCode() { - return regionId.hashCode(); - } - - @SuppressWarnings("unchecked") - final Map entityCounts = MRUMapCache.of(LoadingMap.of( - new EnumMap(EntityType.class), Counter.LOADER - )); - @SuppressWarnings("unchecked") - final Map tileEntityCounts = MRUMapCache.of(LoadingMap.of( - new EnumMap(Material.class), Counter.LOADER - )); - - static class RegionId { - final int x, z; - final long regionId; - RegionId(int x, int z) { - this.x = x >> 5 << 5; - this.z = z >> 5 << 5; - this.regionId = ((long) (this.x) << 32) + (this.z >> 5 << 5) - Integer.MIN_VALUE; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - RegionId regionId1 = (RegionId) o; - - return regionId == regionId1.regionId; - - } - - @Override - public int hashCode() { - return (int) (regionId ^ (regionId >>> 32)); - } - } - } - static void resetTicks(boolean fullReset) { - if (fullReset) { - // Non full is simply for 1 minute reports - timedTicks = 0; - } - lastMinuteTime = System.nanoTime(); - playerTicks = 0; - tileEntityTicks = 0; - entityTicks = 0; - activatedEntityTicks = 0; - } - - Object export() { - return createObject( - pair("s", startTime), - pair("e", endTime), - pair("tk", totalTicks), - pair("tm", totalTime), - pair("w", worlds), - pair("h", toArrayMapper(entries, new Function() { - @Override - public Object apply(TimingHistoryEntry entry) { - TimingData record = entry.data; - if (!record.hasData()) { - return null; - } - return entry.export(); - } - })), - pair("mp", toArrayMapper(minuteReports, new Function() { - @Override - public Object apply(MinuteReport input) { - return input.export(); - } - })) - ); - } - - static class MinuteReport { - final long time = System.currentTimeMillis() / 1000; - - final TicksRecord ticksRecord = new TicksRecord(); - final PingRecord pingRecord = new PingRecord(); - final TimingData fst = TimingsManager.FULL_SERVER_TICK.minuteData.clone(); - final double tps = 1E9 / ( System.nanoTime() - lastMinuteTime ) * ticksRecord.timed; - final double usedMemory = TimingsManager.FULL_SERVER_TICK.avgUsedMemory; - final double freeMemory = TimingsManager.FULL_SERVER_TICK.avgFreeMemory; - final double loadAvg = ManagementFactory.getOperatingSystemMXBean().getSystemLoadAverage(); - - List export() { - return toArray( - time, - Math.round(tps * 100D) / 100D, - Math.round(pingRecord.avg * 100D) / 100D, - fst.export(), - toArray(ticksRecord.timed, - ticksRecord.player, - ticksRecord.entity, - ticksRecord.activatedEntity, - ticksRecord.tileEntity - ), - usedMemory, - freeMemory, - loadAvg - ); - } - } - - private static class TicksRecord { - final long timed; - final long player; - final long entity; - final long tileEntity; - final long activatedEntity; - - TicksRecord() { - timed = timedTicks - (TimingsManager.MINUTE_REPORTS.size() * 1200); - player = playerTicks; - entity = entityTicks; - tileEntity = tileEntityTicks; - activatedEntity = activatedEntityTicks; - } - - } - - private static class PingRecord { - final double avg; - - PingRecord() { - final Collection onlinePlayers = Bukkit.getOnlinePlayers(); - int totalPing = 0; - for (Player player : onlinePlayers) { - totalPing += player.spigot().getPing(); - } - avg = onlinePlayers.isEmpty() ? 0 : totalPing / onlinePlayers.size(); - } - } - - - private static class Counter { - private int count = 0; - @SuppressWarnings({"rawtypes", "SuppressionAnnotation", "Guava"}) - static Function LOADER = new LoadingMap.Feeder() { - @Override - public Counter apply() { - return new Counter(); - } - }; - public int increment() { - return ++count; - } - public int count() { - return count; - } - } -} diff --git a/sources/src/main/java/co/aikar/timings/TimingsManager.java b/sources/src/main/java/co/aikar/timings/TimingsManager.java deleted file mode 100644 index b1349ba8d..000000000 --- a/sources/src/main/java/co/aikar/timings/TimingsManager.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * This file is licensed under the MIT License (MIT). - * - * Copyright (c) 2014 Daniel Ennis - * - * 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 com.google.common.base.Function; -import com.google.common.collect.EvictingQueue; -import org.bukkit.Bukkit; -import org.bukkit.Server; -import org.bukkit.command.Command; -import org.bukkit.plugin.Plugin; -import org.bukkit.plugin.java.PluginClassLoader; -import co.aikar.util.LoadingMap; -import io.akarin.api.internal.Akari; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.logging.Level; - -/** - * Akarin Changes Note - * 1) Thread safe timing (safety) - */ -public final class TimingsManager { - static final Map TIMING_MAP = - Collections.synchronizedMap(LoadingMap.newHashMap( - new Function() { - @Override - public TimingHandler apply(TimingIdentifier id) { - return (id.protect ? - new UnsafeTimingHandler(id) : - new TimingHandler(id) - ); - } - }, - 256, .5F - )); - public static final FullServerTickHandler FULL_SERVER_TICK = new FullServerTickHandler(); - public static final TimingHandler TIMINGS_TICK = Timings.ofSafe("Timings Tick", FULL_SERVER_TICK); - public static final Timing PLUGIN_GROUP_HANDLER = Timings.ofSafe("Plugins"); - public static List hiddenConfigs = new ArrayList(); - public static boolean privacy = false; - - static final Collection HANDLERS = new ArrayDeque(); - static final ArrayDeque MINUTE_REPORTS = new ArrayDeque(); - - static EvictingQueue HISTORY = EvictingQueue.create(12); - static TimingHandler CURRENT; - static long timingStart = 0; - static long historyStart = 0; - static boolean needsFullReset = false; - static boolean needsRecheckEnabled = false; - - private TimingsManager() {} - - /** - * Resets all timing data on the next tick - */ - static void reset() { - needsFullReset = true; - } - - /** - * Ticked every tick by CraftBukkit to count the number of times a timer - * caused TPS loss. - */ - static void tick() { - if (Timings.timingsEnabled) { - boolean violated = FULL_SERVER_TICK.isViolated(); - - Akari.timingsLock.lock(); // Akarin - for (TimingHandler handler : HANDLERS) { - if (handler.isSpecial()) { - // We manually call this - continue; - } - handler.processTick(violated); - } - Akari.timingsLock.unlock(); // Akarin - - TimingHistory.playerTicks += Bukkit.getOnlinePlayers().size(); - TimingHistory.timedTicks++; - // Generate TPS/Ping/Tick reports every minute - } - } - static void stopServer() { - Timings.timingsEnabled = false; - recheckEnabled(); - } - static void recheckEnabled() { - synchronized (TIMING_MAP) { - for (TimingHandler timings : TIMING_MAP.values()) { - timings.checkEnabled(); - } - } - needsRecheckEnabled = false; - } - static void resetTimings() { - if (needsFullReset) { - // Full resets need to re-check every handlers enabled state - // Timing map can be modified from async so we must sync on it. - synchronized (TIMING_MAP) { - for (TimingHandler timings : TIMING_MAP.values()) { - timings.reset(true); - } - } - Bukkit.getLogger().log(Level.INFO, "Timings Reset"); - HISTORY.clear(); - needsFullReset = false; - needsRecheckEnabled = false; - timingStart = System.currentTimeMillis(); - } else { - // Soft resets only need to act on timings that have done something - // Handlers can only be modified on main thread. - Akari.timingsLock.lock(); // Akarin - for (TimingHandler timings : HANDLERS) { - timings.reset(false); - } - Akari.timingsLock.unlock(); // Akarin - } - - Akari.timingsLock.lock(); // Akarin - HANDLERS.clear(); - Akari.timingsLock.unlock(); // Akarin - MINUTE_REPORTS.clear(); - - TimingHistory.resetTicks(true); - historyStart = System.currentTimeMillis(); - } - - static TimingHandler getHandler(String group, String name, Timing parent, boolean protect) { - return TIMING_MAP.get(new TimingIdentifier(group, name, parent, protect)); - } - - - /** - *

Due to access restrictions, we need a helper method to get a Command TimingHandler with String group

- * - * Plugins should never call this - * - * @param pluginName Plugin this command is associated with - * @param command Command to get timings for - * @return TimingHandler - */ - public static Timing getCommandTiming(String pluginName, Command command) { - Plugin plugin = null; - final Server server = Bukkit.getServer(); - if (!( server == null || pluginName == null || - "minecraft".equals(pluginName) || "bukkit".equals(pluginName) || - "spigot".equalsIgnoreCase(pluginName) || "paper".equals(pluginName) - )) { - plugin = server.getPluginManager().getPlugin(pluginName); - } - if (plugin == null) { - // Plugin is passing custom fallback prefix, try to look up by class loader - plugin = getPluginByClassloader(command.getClass()); - } - if (plugin == null) { - return Timings.ofSafe("Command: " + pluginName + ":" + command.getTimingName()); - } - - return Timings.ofSafe(plugin, "Command: " + pluginName + ":" + command.getTimingName()); - } - - /** - * Looks up the class loader for the specified class, and if it is a PluginClassLoader, return the - * Plugin that created this class. - * - * @param clazz Class to check - * @return Plugin if created by a plugin - */ - public static Plugin getPluginByClassloader(Class clazz) { - if (clazz == null) { - return null; - } - final ClassLoader classLoader = clazz.getClassLoader(); - if (classLoader instanceof PluginClassLoader) { - PluginClassLoader pluginClassLoader = (PluginClassLoader) classLoader; - return pluginClassLoader.getPlugin(); - } - return null; - } -} diff --git a/sources/src/main/java/io/akarin/api/internal/Akari.java b/sources/src/main/java/io/akarin/api/internal/Akari.java index 9f796e959..95fec5cc8 100644 --- a/sources/src/main/java/io/akarin/api/internal/Akari.java +++ b/sources/src/main/java/io/akarin/api/internal/Akari.java @@ -7,6 +7,7 @@ import java.util.concurrent.ExecutorCompletionService; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -140,8 +141,6 @@ public abstract class Akari { return serverVersion + " (MC: " + MinecraftServer.getServer().getVersion() + ")"; } - public static final ReentrantSpinningLock timingsLock = new ReentrantSpinningLock(); - /* * Timings */ diff --git a/sources/src/main/java/io/akarin/api/internal/utils/ReentrantSpinningLock.java b/sources/src/main/java/io/akarin/api/internal/utils/ReentrantSpinningLock.java index e05c9f792..a1de55db9 100644 --- a/sources/src/main/java/io/akarin/api/internal/utils/ReentrantSpinningLock.java +++ b/sources/src/main/java/io/akarin/api/internal/utils/ReentrantSpinningLock.java @@ -8,6 +8,7 @@ public class ReentrantSpinningLock { * Impl Note: * A write lock can reentrant as a read lock, while a * read lock is not allowed to reentrant as a write lock. + * READ LOCK IS UNTESTED, USE WITH CATION. */ private final AtomicBoolean writeLocked = new AtomicBoolean(false); @@ -16,7 +17,7 @@ public class ReentrantSpinningLock { private int reentrantLocks = 0; /** - * Lock as a typical write lock + * Lock as a typical reentrant write lock */ public void lock() { long currentThreadId = Thread.currentThread().getId(); @@ -31,9 +32,9 @@ public class ReentrantSpinningLock { public void unlock() { if (reentrantLocks == 0) { heldThreadId = 0; - if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract + //if (readerThreads.get() == 0 || readerThreads.getAndDecrement() == 1) { // Micro-optimization: this saves one subtract writeLocked.set(false); - } + //} } else { --reentrantLocks; } @@ -42,8 +43,9 @@ public class ReentrantSpinningLock { private final AtomicInteger readerThreads = new AtomicInteger(0); /** - * Lock as a typical read lock + * Lock as a typical reentrant read lock */ + @Deprecated public void lockWeak() { long currentThreadId = Thread.currentThread().getId(); if (heldThreadId == currentThreadId) { @@ -57,6 +59,7 @@ public class ReentrantSpinningLock { } } + @Deprecated public void unlockWeak() { if (reentrantLocks == 0) { heldThreadId = 0; @@ -79,6 +82,7 @@ public class ReentrantSpinningLock { } } + @Deprecated public class SpinningReadLock { public void lock() { lockWeak(); @@ -92,7 +96,7 @@ public class ReentrantSpinningLock { return wrappedWriteLock; } - public SpinningReadLock readLocked() { + public SpinningReadLock readLock() { return wrappedReadLock; } } diff --git a/sources/src/main/java/io/akarin/server/core/AkarinSlackScheduler.java b/sources/src/main/java/io/akarin/server/core/AkarinSlackScheduler.java index 5549afd10..454297ae6 100644 --- a/sources/src/main/java/io/akarin/server/core/AkarinSlackScheduler.java +++ b/sources/src/main/java/io/akarin/server/core/AkarinSlackScheduler.java @@ -40,6 +40,7 @@ public class AkarinSlackScheduler extends Thread { MinecraftServer server = MinecraftServer.getServer(); while (server.isRunning()) { + long startProcessTiming = System.currentTimeMillis(); // Send time updates to everyone, it will get the right time from the world the player is in. // Time update, from MinecraftServer#D if (++updateTime >= AkarinGlobalConfig.timeUpdateInterval) { @@ -97,10 +98,9 @@ public class AkarinSlackScheduler extends Thread { } try { - Thread.sleep(100); - } catch (InterruptedException ex) { - Akari.logger.warn("Slack scheduler thread was interrupted unexpectly!"); - ex.printStackTrace(); + Thread.sleep(100 - (System.currentTimeMillis() - startProcessTiming)); + } catch (InterruptedException interrupted) { + ; } } } diff --git a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java index 54287e03a..1904c966f 100644 --- a/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java +++ b/sources/src/main/java/io/akarin/server/mixin/core/MixinTimingHandler.java @@ -2,19 +2,24 @@ package io.akarin.server.mixin.core; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import org.bukkit.Bukkit; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Overwrite; import org.spongepowered.asm.mixin.Shadow; import co.aikar.timings.Timing; +import io.akarin.api.internal.Akari; +import io.akarin.server.core.AkarinGlobalConfig; +import net.minecraft.server.MinecraftServer; @Mixin(targets = "co.aikar.timings.TimingHandler", remap = false) public abstract class MixinTimingHandler { @Shadow @Final String name; @Shadow private boolean enabled; - @Shadow private AtomicLong start; - @Shadow private AtomicInteger timingDepth; + @Shadow private long start; + @Shadow private int timingDepth; @Shadow abstract void addDiff(long diff); @Shadow public abstract Timing startTiming(); @@ -27,9 +32,9 @@ public abstract class MixinTimingHandler { @Overwrite public void stopTimingIfSync() { - //if (Akari.isPrimaryThread(false)) { + if (Akari.isPrimaryThread(false)) { stopTiming(true); // Avoid twice thread check - //} + } } @Overwrite @@ -42,21 +47,17 @@ public abstract class MixinTimingHandler { } public void stopTiming(boolean alreadySync) { - if (!enabled || timingDepth.decrementAndGet() != 0 || start.get() == 0) return; - /*if (!alreadySync) { + if (!enabled || --timingDepth != 0 || start == 0) return; + if (!alreadySync) { Thread curThread = Thread.currentThread(); if (curThread != MinecraftServer.getServer().primaryThread) { - if (false && !AkarinGlobalConfig.silentAsyncTimings) { - Bukkit.getLogger().log(Level.SEVERE, "stopTiming called async for " + name); - Thread.dumpStack(); - } start = 0; return; } - }*/ + } // Safety ensured - long prev = start.getAndSet(0); // Akarin - addDiff(System.nanoTime() - prev); // Akarin + addDiff(System.nanoTime() - start); + start = 0; } }