mirror of https://e.coding.net/circlecloud/Yum.git
117 lines
4.8 KiB
Java
117 lines
4.8 KiB
Java
package pw.yumc.Yum.runnables;
|
|
|
|
import java.lang.Thread.State;
|
|
import java.lang.reflect.Method;
|
|
import java.util.TimerTask;
|
|
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.plugin.Plugin;
|
|
|
|
import pw.yumc.YumCore.bukkit.Log;
|
|
import pw.yumc.YumCore.bukkit.compatible.C;
|
|
import pw.yumc.YumCore.kit.PKit;
|
|
import pw.yumc.YumCore.plugin.protocollib.PacketKit;
|
|
|
|
/**
|
|
* 线程安全检查任务
|
|
*
|
|
* @since 2016年6月22日 下午4:57:32
|
|
* @author 喵♂呜
|
|
*/
|
|
public class MainThreadCheckTask extends TimerTask {
|
|
private static Method tickMethod;
|
|
private String prefix = "§6[§bYum §a线程管理§6] ";
|
|
private String warnPNet = "§6插件 §b%s §c在主线程进行网络操作 §4服务器处于停止状态...";
|
|
private String warnPIO = "§6插件 §b%s §c在主线程进行IO操作 §4服务器处于停止状态...";
|
|
private String warnNet = "§c主线程存在网络操作 §4服务器处于停止状态...";
|
|
private String warnIO = "§c主线程存在IO操作 §4服务器处于停止状态...";
|
|
private String deliver = "§c服务器处于停止状态 已超过 %s 秒 激活心跳 防止线程关闭...";
|
|
private int stopTime = 0;
|
|
private Thread mainThread;
|
|
|
|
static {
|
|
try {
|
|
Class clazz = Class.forName("org.spigotmc.WatchdogThread");
|
|
tickMethod = clazz.getDeclaredMethod("tick");
|
|
} catch (Exception ignored) {
|
|
}
|
|
}
|
|
|
|
public MainThreadCheckTask(Thread mainThread) {
|
|
this.mainThread = mainThread;
|
|
}
|
|
|
|
@Override
|
|
public void run() {
|
|
// According to this post the thread is still in Runnable although it's waiting for
|
|
// file/http ressources
|
|
// https://stackoverflow.com/questions/20795295/why-jstack-out-says-thread-state-is-runnable-while-socketread
|
|
if (mainThread.getState() == State.RUNNABLE) {
|
|
// Based on this post we have to check the top element of the stack
|
|
// https://stackoverflow.com/questions/20891386/how-to-detect-thread-being-blocked-by-io
|
|
StackTraceElement[] stackTrace = mainThread.getStackTrace();
|
|
StackTraceElement topElement = stackTrace[0];
|
|
if (topElement.isNativeMethod()) {
|
|
// Socket/SQL (connect) - java.net.DualStackPlainSocketImpl.connect0
|
|
// Socket/SQL (read) - java.net.SocketInputStream.socketRead0
|
|
// Socket/SQL (write) - java.net.SocketOutputStream.socketWrite0
|
|
if (isElementEqual(topElement, "java.net.DualStackPlainSocketImpl", "connect0")
|
|
|| isElementEqual(topElement, "java.net.SocketInputStream", "socketRead0")
|
|
|| isElementEqual(topElement, "java.net.SocketOutputStream", "socketWrite0")) {
|
|
Plugin plugin = PKit.getOperatePlugin(stackTrace);
|
|
if (plugin != null) {
|
|
Log.console(prefix + warnPNet, plugin.getName());
|
|
} else {
|
|
Log.console(prefix + warnNet);
|
|
}
|
|
tick();
|
|
}
|
|
// File (in) - java.io.FileInputStream.readBytes
|
|
// File (out) - java.io.FileOutputStream.writeBytes
|
|
else if (isElementEqual(topElement, "java.io.FileInputStream", "readBytes")
|
|
|| isElementEqual(topElement, "java.io.FileOutputStream", "writeBytes")) {
|
|
Plugin plugin = PKit.getOperatePlugin(stackTrace);
|
|
if (plugin != null) {
|
|
Log.console(prefix + warnPIO, plugin.getName());
|
|
} else {
|
|
Log.console(prefix + warnIO);
|
|
}
|
|
tick();
|
|
} else {
|
|
stopTime = 0;
|
|
}
|
|
} else {
|
|
stopTime = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isElementEqual(StackTraceElement traceElement, String className, String methodName) {
|
|
return traceElement.getClassName().equals(className) && traceElement.getMethodName().equals(methodName);
|
|
}
|
|
|
|
private void tick() {
|
|
stopTime += 5;
|
|
if (stopTime >= 45) {
|
|
Log.console(prefix + deliver, stopTime);
|
|
wttick();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 保持服务器心跳
|
|
*/
|
|
public static void wttick() {
|
|
try {
|
|
if (tickMethod != null) {
|
|
tickMethod.invoke(null);
|
|
}
|
|
for (final Player player : C.Player.getOnlinePlayers()) {
|
|
player.sendMessage("§4注意: §c服务器主线程处于停止状态 请等待操作完成!");
|
|
PacketKit.keep_live(player);
|
|
}
|
|
} catch (final Throwable e) {
|
|
}
|
|
}
|
|
}
|