Browse Source

Dead lock detector thanks Nemesiss/L2M. There's a config option for it (disabled by default).

Sami 16 years ago
parent
commit
3690e60a62

+ 4 - 0
L2_GameServer/java/config/General.properties

@@ -139,6 +139,10 @@ AiMaxThread = 6
 #Packet LifeTime in milliseconds, 0 - disabled feature
 #Packet LifeTime in milliseconds, 0 - disabled feature
 PacketLifeTime = 0
 PacketLifeTime = 0
 
 
+#Dead Lock Detector (a separate thread for detecting deadlocks)
+#for improved crash logs and automatic restart in deadlock case (default behavior)
+DeadLockDetector = False
+
 #============================================================#
 #============================================================#
 #                         Optimize                           #
 #                         Optimize                           #
 #============================================================#
 #============================================================#

+ 2 - 0
L2_GameServer/java/net/sf/l2j/Config.java

@@ -342,6 +342,7 @@ public final class Config
     public static int		GENERAL_THREAD_CORE_SIZE;
     public static int		GENERAL_THREAD_CORE_SIZE;
     public static int		AI_MAX_THREAD;
     public static int		AI_MAX_THREAD;
     public static int		PACKET_LIFETIME;
     public static int		PACKET_LIFETIME;
+    public static boolean	DEADLOCK_DETECTOR;
     public static int		FLOODPROTECTOR_INITIALSIZE;
     public static int		FLOODPROTECTOR_INITIALSIZE;
     public static boolean	ALLOW_DISCARDITEM;
     public static boolean	ALLOW_DISCARDITEM;
     public static int		AUTODESTROY_ITEM_AFTER;
     public static int		AUTODESTROY_ITEM_AFTER;
@@ -1352,6 +1353,7 @@ public final class Config
                 GENERAL_THREAD_CORE_SIZE					= Integer.parseInt(General.getProperty("GeneralThreadCoreSize", "4"));
                 GENERAL_THREAD_CORE_SIZE					= Integer.parseInt(General.getProperty("GeneralThreadCoreSize", "4"));
                 AI_MAX_THREAD								= Integer.parseInt(General.getProperty("AiMaxThread", "10"));
                 AI_MAX_THREAD								= Integer.parseInt(General.getProperty("AiMaxThread", "10"));
                 PACKET_LIFETIME								= Integer.parseInt(General.getProperty("PacketLifeTime", "0"));
                 PACKET_LIFETIME								= Integer.parseInt(General.getProperty("PacketLifeTime", "0"));
+                DEADLOCK_DETECTOR							= Boolean.parseBoolean(General.getProperty("DeadLockDetector", "False"));
                 FLOODPROTECTOR_INITIALSIZE					= Integer.parseInt(General.getProperty("FloodProtectorInitialSize", "50"));
                 FLOODPROTECTOR_INITIALSIZE					= Integer.parseInt(General.getProperty("FloodProtectorInitialSize", "50"));
                 ALLOW_DISCARDITEM							= Boolean.parseBoolean(General.getProperty("AllowDiscardItem", "True"));
                 ALLOW_DISCARDITEM							= Boolean.parseBoolean(General.getProperty("AllowDiscardItem", "True"));
                 AUTODESTROY_ITEM_AFTER						= Integer.parseInt(General.getProperty("AutoDestroyDroppedItemAfter", "0"));
                 AUTODESTROY_ITEM_AFTER						= Integer.parseInt(General.getProperty("AutoDestroyDroppedItemAfter", "0"));

+ 11 - 0
L2_GameServer/java/net/sf/l2j/gameserver/GameServer.java

@@ -110,6 +110,7 @@ import net.sf.l2j.gameserver.taskmanager.TaskManager;
 import net.sf.l2j.gameserver.util.DynamicExtension;
 import net.sf.l2j.gameserver.util.DynamicExtension;
 import net.sf.l2j.gameserver.util.FloodProtector;
 import net.sf.l2j.gameserver.util.FloodProtector;
 import net.sf.l2j.status.Status;
 import net.sf.l2j.status.Status;
+import net.sf.l2j.util.DeadLockDetector;
 
 
 import org.mmocore.network.SelectorConfig;
 import org.mmocore.network.SelectorConfig;
 import org.mmocore.network.SelectorThread;
 import org.mmocore.network.SelectorThread;
@@ -124,6 +125,7 @@ public class GameServer
 	private static final Logger _log = Logger.getLogger(GameServer.class.getName());
 	private static final Logger _log = Logger.getLogger(GameServer.class.getName());
 	
 	
 	private final SelectorThread<L2GameClient> _selectorThread;
 	private final SelectorThread<L2GameClient> _selectorThread;
+	private final DeadLockDetector _deadDetectThread;
 	private final SkillTable _skillTable;
 	private final SkillTable _skillTable;
 	private final ItemTable _itemTable;
 	private final ItemTable _itemTable;
 	private final NpcTable _npcTable;
 	private final NpcTable _npcTable;
@@ -153,6 +155,11 @@ public class GameServer
 		return _selectorThread;
 		return _selectorThread;
 	}
 	}
 	
 	
+	public DeadLockDetector getDeadLockDetectorThread()
+	{
+		return _deadDetectThread;
+	}
+	
 	public ClanHallManager getCHManager()
 	public ClanHallManager getCHManager()
 	{
 	{
 		return _cHManager;
 		return _cHManager;
@@ -442,6 +449,10 @@ public class GameServer
 		FloodProtector.getInstance();
 		FloodProtector.getInstance();
 		TvTManager.getInstance();
 		TvTManager.getInstance();
 		KnownListUpdateTaskManager.getInstance();
 		KnownListUpdateTaskManager.getInstance();
+		if (Config.DEADLOCK_DETECTOR)
+			_deadDetectThread = new DeadLockDetector(20, DeadLockDetector.RESTART);
+		else
+			_deadDetectThread = null;
 		System.gc();
 		System.gc();
 		// maxMemory is the upper limit the jvm can use, totalMemory the size of
 		// maxMemory is the upper limit the jvm can use, totalMemory the size of
 		// the current allocation pool, freeMemory the unused memory in the
 		// the current allocation pool, freeMemory the unused memory in the

+ 2 - 0
L2_GameServer/java/net/sf/l2j/gameserver/Shutdown.java

@@ -209,6 +209,8 @@ public class Shutdown extends Thread
 			try
 			try
 			{
 			{
 				GameTimeController.getInstance().stopTimer();
 				GameTimeController.getInstance().stopTimer();
+				if (Config.DEADLOCK_DETECTOR)
+					GameServer.gameServer.getDeadLockDetectorThread().interrupt();
 			}
 			}
 			catch (Throwable t)
 			catch (Throwable t)
 			{
 			{

+ 118 - 0
L2_GameServer/java/net/sf/l2j/util/DeadLockDetector.java

@@ -0,0 +1,118 @@
+/* This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ * 
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+package net.sf.l2j.util;
+
+import java.lang.management.LockInfo;
+import java.lang.management.ManagementFactory;
+import java.lang.management.MonitorInfo;
+import java.lang.management.ThreadInfo;
+import java.lang.management.ThreadMXBean;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import net.sf.l2j.gameserver.Shutdown;
+import net.sf.l2j.gameserver.Announcements;
+
+/**
+ * @author -Nemesiss- L2M
+ *
+ */
+public class DeadLockDetector extends Thread
+{
+	private static Logger _log = Logger.getLogger(DeadLockDetector.class.getName());
+
+	public static final byte NOTHING = 0;
+	public static final byte RESTART = 1;
+	public static final byte REPAIR = 2;
+
+	private final int _sleepTime;
+
+	private final ThreadMXBean tmx;
+
+	private final byte _doWhenDL;
+	public DeadLockDetector(int sleepTime, byte doWhenDL)
+	{
+		super("DeadLockDetector");
+		_sleepTime = sleepTime*1000;
+		tmx = ManagementFactory.getThreadMXBean();
+		_doWhenDL = doWhenDL;
+	}
+
+	@Override 
+	public final void run()
+	{
+		boolean deadlock = false;
+		while(!deadlock && !this.isInterrupted())
+		{
+			try
+			{
+				long[] ids = tmx.findDeadlockedThreads();
+
+				if(ids != null)
+				{
+					deadlock = true;
+					ThreadInfo[] tis = tmx.getThreadInfo(ids,true,true);
+					String info = "DeadLock Found!\n";
+					for(ThreadInfo ti : tis)
+					{
+						info += ti.toString();
+					}
+
+					for(ThreadInfo ti : tis)
+					{
+						LockInfo[] locks = ti.getLockedSynchronizers();
+						MonitorInfo[] monitors = ti.getLockedMonitors();
+						if(locks.length == 0 && monitors.length == 0)
+						{
+							continue;
+						}
+
+						ThreadInfo dl = ti;
+						info += "Java-level deadlock:\n";
+						info += "\t"+dl.getThreadName()+" is waiting to lock "+dl.getLockInfo().toString()+" which is held by "+dl.getLockOwnerName()+"\n";
+						while((dl = tmx.getThreadInfo(new long[]{dl.getLockOwnerId()},true,true)[0]).getThreadId() != ti.getThreadId())
+						{
+							info += "\t"+dl.getThreadName()+" is waiting to lock "+dl.getLockInfo().toString()+" which is held by "+dl.getLockOwnerName()+"\n";
+						}
+					}
+					_log.warning(info);
+
+					if(_doWhenDL == RESTART)
+					{
+						Announcements an = Announcements.getInstance();
+						an.announceToAll("Server has stability issues - restarting now.");
+						Shutdown.getInstance().startTelnetShutdown("DeadLockDetector - Auto Restart",60,true);
+					}
+					else if(_doWhenDL == REPAIR)
+					{
+						Announcements an = Announcements.getInstance();
+						an.announceToAll("Server has stability issues - restarting now.");
+						Shutdown.getInstance().startTelnetShutdown("DeadLockDetector - Auto Restart",60,true);
+					}
+					
+				}
+				Thread.sleep(_sleepTime); 
+			}
+			catch(InterruptedException e)
+			{
+				//
+			}
+			catch(Exception e)
+			{
+				_log.log(Level.WARNING,"DeadLockDetector: ",e);
+			}
+		}
+	}
+}