summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattes D <github@xoft.cz>2017-01-18 10:51:17 +0100
committerGitHub <noreply@github.com>2017-01-18 10:51:17 +0100
commit0256daa7ca98bab7482581dad39f66d570030874 (patch)
treede9232cbf239800ea1e7a71cf52086509a9472ea
parentUpdated Github label links (#3543) (diff)
parentDeadlockDetect now lists some tracked CS's stats. (diff)
downloadcuberite-0256daa7ca98bab7482581dad39f66d570030874.tar
cuberite-0256daa7ca98bab7482581dad39f66d570030874.tar.gz
cuberite-0256daa7ca98bab7482581dad39f66d570030874.tar.bz2
cuberite-0256daa7ca98bab7482581dad39f66d570030874.tar.lz
cuberite-0256daa7ca98bab7482581dad39f66d570030874.tar.xz
cuberite-0256daa7ca98bab7482581dad39f66d570030874.tar.zst
cuberite-0256daa7ca98bab7482581dad39f66d570030874.zip
-rw-r--r--Server/Plugins/Debuggers/Debuggers.lua28
-rw-r--r--Server/Plugins/Debuggers/Info.lua6
-rw-r--r--src/Bindings/LuaState.cpp19
-rw-r--r--src/Bindings/LuaState.h7
-rw-r--r--src/Bindings/PluginLua.cpp7
-rw-r--r--src/Bindings/PluginLua.h5
-rw-r--r--src/Bindings/PluginManager.cpp31
-rw-r--r--src/Bindings/PluginManager.h6
-rw-r--r--src/ChunkMap.cpp19
-rw-r--r--src/ChunkMap.h7
-rw-r--r--src/DeadlockDetect.cpp73
-rw-r--r--src/DeadlockDetect.h26
-rw-r--r--src/OSSupport/CriticalSection.cpp24
-rw-r--r--src/OSSupport/CriticalSection.h39
-rw-r--r--src/Root.cpp14
-rw-r--r--src/Root.h5
-rw-r--r--src/World.cpp16
-rw-r--r--src/World.h12
-rw-r--r--tests/LoadablePieces/Stubs.cpp17
-rw-r--r--tests/LuaThreadStress/Stubs.cpp17
20 files changed, 321 insertions, 57 deletions
diff --git a/Server/Plugins/Debuggers/Debuggers.lua b/Server/Plugins/Debuggers/Debuggers.lua
index 28b7e254d..c433148ca 100644
--- a/Server/Plugins/Debuggers/Debuggers.lua
+++ b/Server/Plugins/Debuggers/Debuggers.lua
@@ -2420,6 +2420,34 @@ end
+function HandleConsoleDeadlock(a_Split)
+ -- If given a parameter, assume it's a world name and simulate a deadlock in the world's tick thread
+ if (a_Split[2]) then
+ local world = cRoot:Get():GetWorld(a_Split[2])
+ if (world) then
+ world:ScheduleTask(0,
+ function()
+ -- Make a live-lock:
+ while (true) do
+ end
+ end
+ )
+ return true, "Deadlock in world tick thread for world " .. a_Split[2] .. " has been scheduled."
+ end
+ LOG("Not a world name: " .. a_Split[2] .. "; simulating a deadlock in the command execution thread instead.")
+ else
+ LOG("Simulating a deadlock in the command execution thread.")
+ end
+
+ -- Make a live-lock in the command execution thread:
+ while(true) do
+ end
+end
+
+
+
+
+
function HandleConsoleDownload(a_Split)
-- Check params:
local url = a_Split[2]
diff --git a/Server/Plugins/Debuggers/Info.lua b/Server/Plugins/Debuggers/Info.lua
index a29ab5995..028f7a70b 100644
--- a/Server/Plugins/Debuggers/Info.lua
+++ b/Server/Plugins/Debuggers/Info.lua
@@ -266,6 +266,12 @@ g_PluginInfo =
HelpString = "Performs cBoundingBox API tests",
},
+ ["deadlock"] =
+ {
+ Handler = HandleConsoleDeadlock,
+ HelpString = "Simulates a deadlock, either on the command execution thread, or on a world tick thread",
+ },
+
["download"] =
{
Handler = HandleConsoleDownload,
diff --git a/src/Bindings/LuaState.cpp b/src/Bindings/LuaState.cpp
index 2acf7df84..ec63d2767 100644
--- a/src/Bindings/LuaState.cpp
+++ b/src/Bindings/LuaState.cpp
@@ -19,6 +19,7 @@ extern "C"
#include "LuaJson.h"
#include "../Entities/Entity.h"
#include "../BlockEntities/BlockEntity.h"
+#include "../DeadlockDetect.h"
@@ -2225,6 +2226,24 @@ void cLuaState::LogApiCallParamFailure(const char * a_FnName, const char * a_Par
+void cLuaState::TrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect)
+{
+ a_DeadlockDetect.TrackCriticalSection(m_CS, Printf("cLuaState %s", m_SubsystemName.c_str()));
+}
+
+
+
+
+
+void cLuaState::UntrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect)
+{
+ a_DeadlockDetect.UntrackCriticalSection(m_CS);
+}
+
+
+
+
+
int cLuaState::ReportFnCallErrors(lua_State * a_LuaState)
{
LOGWARNING("LUA: %s", lua_tostring(a_LuaState, -1));
diff --git a/src/Bindings/LuaState.h b/src/Bindings/LuaState.h
index 558e8d79a..1a56c18ff 100644
--- a/src/Bindings/LuaState.h
+++ b/src/Bindings/LuaState.h
@@ -46,6 +46,7 @@ class cLuaServerHandle;
class cLuaTCPLink;
class cLuaUDPEndpoint;
class cPluginLua;
+class cDeadlockDetect;
@@ -822,6 +823,12 @@ public:
logs the stack trace and stack values. */
void LogApiCallParamFailure(const char * a_FnName, const char * a_ParamNames);
+ /** Adds this object's CS to the DeadlockDetect's tracked CSs. */
+ void TrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect);
+
+ /** Removes this object's CS from the DeadlockDetect's tracked CSs. */
+ void UntrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect);
+
protected:
cCriticalSection m_CS;
diff --git a/src/Bindings/PluginLua.cpp b/src/Bindings/PluginLua.cpp
index f2896abde..202477962 100644
--- a/src/Bindings/PluginLua.cpp
+++ b/src/Bindings/PluginLua.cpp
@@ -33,10 +33,12 @@ extern "C"
////////////////////////////////////////////////////////////////////////////////
// cPluginLua:
-cPluginLua::cPluginLua(const AString & a_PluginDirectory) :
+cPluginLua::cPluginLua(const AString & a_PluginDirectory, cDeadlockDetect & a_DeadlockDetect) :
cPlugin(a_PluginDirectory),
- m_LuaState(Printf("plugin %s", a_PluginDirectory.c_str()))
+ m_LuaState(Printf("plugin %s", a_PluginDirectory.c_str())),
+ m_DeadlockDetect(a_DeadlockDetect)
{
+ m_LuaState.TrackInDeadlockDetect(a_DeadlockDetect);
}
@@ -46,6 +48,7 @@ cPluginLua::cPluginLua(const AString & a_PluginDirectory) :
cPluginLua::~cPluginLua()
{
Close();
+ m_LuaState.UntrackInDeadlockDetect(m_DeadlockDetect);
}
diff --git a/src/Bindings/PluginLua.h b/src/Bindings/PluginLua.h
index dc3c91880..703cb8ead 100644
--- a/src/Bindings/PluginLua.h
+++ b/src/Bindings/PluginLua.h
@@ -62,7 +62,7 @@ public:
- cPluginLua(const AString & a_PluginDirectory);
+ cPluginLua(const AString & a_PluginDirectory, cDeadlockDetect & a_DeadlockDetect);
~cPluginLua();
virtual void OnDisable(void) override;
@@ -179,6 +179,9 @@ protected:
/** Hooks that the plugin has registered. */
cHookMap m_HookMap;
+ /** The DeadlockDetect object to which the plugin's CS is tracked. */
+ cDeadlockDetect & m_DeadlockDetect;
+
/** Releases all Lua references, notifies and removes all m_Resettables[] and closes the m_LuaState. */
void Close(void);
diff --git a/src/Bindings/PluginManager.cpp b/src/Bindings/PluginManager.cpp
index 19d2e8b4d..e190abe15 100644
--- a/src/Bindings/PluginManager.cpp
+++ b/src/Bindings/PluginManager.cpp
@@ -32,8 +32,9 @@ cPluginManager * cPluginManager::Get(void)
-cPluginManager::cPluginManager(void) :
- m_bReloadPlugins(false)
+cPluginManager::cPluginManager(cDeadlockDetect & a_DeadlockDetect) :
+ m_bReloadPlugins(false),
+ m_DeadlockDetect(a_DeadlockDetect)
{
}
@@ -98,7 +99,7 @@ void cPluginManager::RefreshPluginList(void)
} // for plugin - m_Plugins[]
if (!hasFound)
{
- m_Plugins.push_back(std::make_shared<cPluginLua>(folder));
+ m_Plugins.push_back(std::make_shared<cPluginLua>(folder, m_DeadlockDetect));
}
} // for folder - Folders[]
}
@@ -601,6 +602,30 @@ bool cPluginManager::CallHookEntityChangedWorld(cEntity & a_Entity, cWorld & a_W
bool cPluginManager::CallHookExecuteCommand(cPlayer * a_Player, const AStringVector & a_Split, const AString & a_EntireCommand, CommandResult & a_Result)
{
+ // Output the command being executed to log (for troubleshooting deadlocks-in-commands):
+ if (a_Player != nullptr)
+ {
+ auto world = a_Player->GetWorld();
+ AString worldName;
+ Int64 worldAge;
+ if (world != nullptr)
+ {
+ worldName = world->GetName();
+ worldAge = world->GetWorldAge();
+ }
+ else
+ {
+ worldName = "<no world>";
+ worldAge = 0;
+ }
+ LOG("Player %s is executing command \"%s\" in world \"%s\" at world age %lld.",
+ a_Player->GetName().c_str(),
+ a_EntireCommand.c_str(),
+ worldName.c_str(),
+ worldAge
+ );
+ }
+
FIND_HOOK(HOOK_EXECUTE_COMMAND);
VERIFY_HOOK;
diff --git a/src/Bindings/PluginManager.h b/src/Bindings/PluginManager.h
index 0423d6af1..7c818ca2d 100644
--- a/src/Bindings/PluginManager.h
+++ b/src/Bindings/PluginManager.h
@@ -26,6 +26,7 @@ class cPlugin;
class cProjectileEntity;
class cWorld;
class cSettingsRepositoryInterface;
+class cDeadlockDetect;
struct TakeDamageInfo;
typedef SharedPtr<cPlugin> cPluginPtr;
@@ -413,8 +414,11 @@ private:
/** If set to true, all the plugins will be reloaded within the next call to Tick(). */
bool m_bReloadPlugins;
+ /** The deadlock detect in which all plugins should track their CSs. */
+ cDeadlockDetect & m_DeadlockDetect;
- cPluginManager();
+
+ cPluginManager(cDeadlockDetect & a_DeadlockDetect);
virtual ~cPluginManager();
/** Reloads all plugins, defaulting to settings.ini for settings location */
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index ee097f59e..cb3a8cb87 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -19,6 +19,7 @@
#include "SetChunkData.h"
#include "Blocks/ChunkInterface.h"
#include "Entities/Pickup.h"
+#include "DeadlockDetect.h"
#ifndef _WIN32
#include <cstdlib> // abs
@@ -2783,6 +2784,24 @@ void cChunkMap::SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTi
+void cChunkMap::TrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect, const AString & a_WorldName)
+{
+ a_DeadlockDetect.TrackCriticalSection(m_CSChunks, Printf("World %s chunkmap", a_WorldName.c_str()));
+}
+
+
+
+
+
+void cChunkMap::UntrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect)
+{
+ a_DeadlockDetect.UntrackCriticalSection(m_CSChunks);
+}
+
+
+
+
+
void cChunkMap::FastSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
int ChunkX, ChunkZ, X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 2272567af..f1631f91b 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -38,6 +38,7 @@ class cMobCensus;
class cMobSpawner;
class cSetChunkData;
class cBoundingBox;
+class cDeadlockDetect;
typedef std::list<cClientHandle *> cClientHandleList;
typedef cChunk * cChunkPtr;
@@ -411,6 +412,12 @@ public:
as at least one requests is active the chunk will be ticked). */
void SetChunkAlwaysTicked(int a_ChunkX, int a_ChunkZ, bool a_AlwaysTicked);
+ /** Adds this chunkmap's CS to the DeadlockDetect's tracked CSs. */
+ void TrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect, const AString & a_WorldName);
+
+ /** Removes this chunkmap's CS from the DeadlockDetect's tracked CSs. */
+ void UntrackInDeadlockDetect(cDeadlockDetect & a_DeadlockDetect);
+
private:
// The chunks can manipulate neighbors while in their Tick() method, using LockedGetBlock() and LockedSetBlock()
diff --git a/src/DeadlockDetect.cpp b/src/DeadlockDetect.cpp
index 3141020d0..df14e610b 100644
--- a/src/DeadlockDetect.cpp
+++ b/src/DeadlockDetect.cpp
@@ -30,6 +30,27 @@ cDeadlockDetect::cDeadlockDetect(void) :
+cDeadlockDetect::~cDeadlockDetect()
+{
+ // Check that all tracked CSs have been removed, report any remaining:
+ cCSLock lock(m_CS);
+ if (!m_TrackedCriticalSections.empty())
+ {
+ LOGWARNING("DeadlockDetect: Some CS objects (%u) haven't been removed from tracking", static_cast<unsigned>(m_TrackedCriticalSections.size()));
+ for (const auto & tcs: m_TrackedCriticalSections)
+ {
+ LOGWARNING(" CS %p / %s",
+ static_cast<void *>(tcs.first),
+ tcs.second.c_str()
+ );
+ }
+ }
+}
+
+
+
+
+
bool cDeadlockDetect::Start(int a_IntervalSec)
{
m_IntervalSec = a_IntervalSec;
@@ -61,6 +82,33 @@ bool cDeadlockDetect::Start(int a_IntervalSec)
+void cDeadlockDetect::TrackCriticalSection(cCriticalSection & a_CS, const AString & a_Name)
+{
+ cCSLock lock(m_CS);
+ m_TrackedCriticalSections.emplace_back(std::make_pair(&a_CS, a_Name));
+}
+
+
+
+
+
+void cDeadlockDetect::UntrackCriticalSection(cCriticalSection & a_CS)
+{
+ cCSLock lock(m_CS);
+ for (auto itr = m_TrackedCriticalSections.begin(), end = m_TrackedCriticalSections.end(); itr != end; ++itr)
+ {
+ if (itr->first == &a_CS)
+ {
+ m_TrackedCriticalSections.erase(itr);
+ return;
+ }
+ }
+}
+
+
+
+
+
void cDeadlockDetect::Execute(void)
{
// Loop until the signal to terminate:
@@ -121,7 +169,7 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
WorldAge.m_NumCyclesSame += 1;
if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS)
{
- DeadlockDetected();
+ DeadlockDetected(a_WorldName, a_Age);
}
}
else
@@ -135,9 +183,12 @@ void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
-void cDeadlockDetect::DeadlockDetected(void)
+void cDeadlockDetect::DeadlockDetected(const AString & a_WorldName, Int64 a_WorldAge)
{
- LOGERROR("Deadlock detected, aborting the server");
+ LOGERROR("Deadlock detected: world %s has been stuck at age %lld. Aborting the server.",
+ a_WorldName.c_str(), static_cast<long long>(a_WorldAge)
+ );
+ ListTrackedCSs();
ASSERT(!"Deadlock detected");
abort();
}
@@ -145,3 +196,19 @@ void cDeadlockDetect::DeadlockDetected(void)
+
+void cDeadlockDetect::ListTrackedCSs(void)
+{
+ cCSLock lock(m_CS);
+ for (const auto & cs: m_TrackedCriticalSections)
+ {
+ LOG("CS at %p, %s: RecursionCount = %d, ThreadIDHash = %04llx",
+ static_cast<void *>(cs.first), cs.second.c_str(),
+ cs.first->m_RecursionCount, static_cast<UInt64>(std::hash<std::thread::id>()(cs.first->m_OwningThreadID))
+ );
+ }
+}
+
+
+
+
diff --git a/src/DeadlockDetect.h b/src/DeadlockDetect.h
index 39d3f8691..ee6dc3e22 100644
--- a/src/DeadlockDetect.h
+++ b/src/DeadlockDetect.h
@@ -27,10 +27,20 @@ class cDeadlockDetect :
public:
cDeadlockDetect(void);
+ ~cDeadlockDetect();
/** Starts the detection. Hides cIsThread's Start, because we need some initialization */
bool Start(int a_IntervalSec);
+ /** Adds the critical section for tracking.
+ Tracked CSs are listed, together with ownership details, when a deadlock is detected.
+ A tracked CS must be untracked before it is destroyed.
+ a_Name is an arbitrary name that is listed along with the CS in the output. */
+ void TrackCriticalSection(cCriticalSection & a_CS, const AString & a_Name);
+
+ /** Removes the CS from the tracking. */
+ void UntrackCriticalSection(cCriticalSection & a_CS);
+
protected:
struct sWorldAge
{
@@ -44,6 +54,13 @@ protected:
/** Maps world name -> sWorldAge */
typedef std::map<AString, sWorldAge> WorldAges;
+ /** Protects m_TrackedCriticalSections from multithreaded access. */
+ cCriticalSection m_CS;
+
+ /** CriticalSections that are tracked (their status output on deadlock).
+ Protected against multithreaded access by m_CS. */
+ std::vector<std::pair<cCriticalSection *, AString>> m_TrackedCriticalSections;
+
WorldAges m_WorldAges;
/** Number of secods for which the ages must be the same for the detection to trigger */
@@ -59,8 +76,13 @@ protected:
/** Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadlock detected */
void CheckWorldAge(const AString & a_WorldName, Int64 a_Age);
- /** Called when a deadlock is detected. Aborts the server. */
- NORETURN void DeadlockDetected(void);
+ /** Called when a deadlock is detected in a world. Aborts the server.
+ a_WorldName is the name of the world whose age has triggered the detection.
+ a_WorldAge is the age (in ticks) in which the world is stuck. */
+ NORETURN void DeadlockDetected(const AString & a_WorldName, Int64 a_WorldAge);
+
+ /** Outputs a listing of the tracked CSs, together with their name and state. */
+ void ListTrackedCSs();
} ;
diff --git a/src/OSSupport/CriticalSection.cpp b/src/OSSupport/CriticalSection.cpp
index 2e533676d..27284acb0 100644
--- a/src/OSSupport/CriticalSection.cpp
+++ b/src/OSSupport/CriticalSection.cpp
@@ -9,12 +9,10 @@
////////////////////////////////////////////////////////////////////////////////
// cCriticalSection:
-#ifdef _DEBUG
-cCriticalSection::cCriticalSection()
+cCriticalSection::cCriticalSection():
+ m_RecursionCount(0)
{
- m_IsLocked = 0;
}
-#endif // _DEBUG
@@ -24,10 +22,8 @@ void cCriticalSection::Lock()
{
m_Mutex.lock();
- #ifdef _DEBUG
- m_IsLocked += 1;
- m_OwningThreadID = std::this_thread::get_id();
- #endif // _DEBUG
+ m_RecursionCount += 1;
+ m_OwningThreadID = std::this_thread::get_id();
}
@@ -36,10 +32,8 @@ void cCriticalSection::Lock()
void cCriticalSection::Unlock()
{
- #ifdef _DEBUG
- ASSERT(m_IsLocked > 0);
- m_IsLocked -= 1;
- #endif // _DEBUG
+ ASSERT(IsLockedByCurrentThread());
+ m_RecursionCount -= 1;
m_Mutex.unlock();
}
@@ -48,10 +42,9 @@ void cCriticalSection::Unlock()
-#ifdef _DEBUG
bool cCriticalSection::IsLocked(void)
{
- return (m_IsLocked > 0);
+ return (m_RecursionCount > 0);
}
@@ -60,9 +53,8 @@ bool cCriticalSection::IsLocked(void)
bool cCriticalSection::IsLockedByCurrentThread(void)
{
- return ((m_IsLocked > 0) && (m_OwningThreadID == std::this_thread::get_id()));
+ return ((m_RecursionCount > 0) && (m_OwningThreadID == std::this_thread::get_id()));
}
-#endif // _DEBUG
diff --git a/src/OSSupport/CriticalSection.h b/src/OSSupport/CriticalSection.h
index 6ea18051a..917957aeb 100644
--- a/src/OSSupport/CriticalSection.h
+++ b/src/OSSupport/CriticalSection.h
@@ -9,27 +9,40 @@
class cCriticalSection
{
+ friend class cDeadlockDetect; // Allow the DeadlockDetect to read the internals, so that it may output some statistics
+
public:
void Lock(void);
void Unlock(void);
- // IsLocked / IsLockedByCurrentThread are only used in ASSERT statements, but because of the changes with ASSERT they must always be defined
- // The fake versions (in Release) will not effect the program in any way
- #ifdef _DEBUG
- cCriticalSection(void);
- bool IsLocked(void);
- bool IsLockedByCurrentThread(void);
- #else
- bool IsLocked(void) { return false; }
- bool IsLockedByCurrentThread(void) { return false; }
- #endif // _DEBUG
+ cCriticalSection(void);
+
+ /** Returns true if the CS is currently locked.
+ Note that since it relies on the m_RecursionCount value, it is inherently thread-unsafe, prone to false positives.
+ Also, due to multithreading, the state can change between this when function is evaluated and the returned value is used.
+ To be used in ASSERT(IsLocked()) only. */
+ bool IsLocked(void);
+
+ /** Returns true if the CS is currently locked by the thread calling this function.
+ Note that since it relies on the m_RecursionCount value, it is inherently thread-unsafe, prone to false positives.
+ Also, due to multithreading, the state can change between this when function is evaluated and the returned value is used.
+ To be used in ASSERT(IsLockedByCurrentThread()) only. */
+ bool IsLockedByCurrentThread(void);
private:
- #ifdef _DEBUG
- int m_IsLocked; // Number of times this CS is locked
+ /** Number of times that this CS is currently locked (levels of recursion). Zero if not locked.
+ Note that this value should be considered true only when the CS is locked; without the lock, it is UndefinedBehavior to even read it,
+ but making it std::atomic would impose too much of a runtime penalty.
+ It is only ever read without the lock in the DeadlockDetect, where the server is terminating anyway. */
+ int m_RecursionCount;
+
+ /** ID of the thread that is currently holding the CS.
+ Note that this value should be considered true only when the CS is locked; without the lock, it is UndefinedBehavior to even read it,
+ but making it std::atomic would impose too much of a runtime penalty.
+ When unlocked, the value stored here has no meaning, it may be an ID of a previous holder, or it could be any garbage.
+ It is only ever read without the lock in the DeadlockDetect, where the server is terminating anyway. */
std::thread::id m_OwningThreadID;
- #endif // _DEBUG
std::recursive_mutex m_Mutex;
} ALIGN_8;
diff --git a/src/Root.cpp b/src/Root.cpp
index 8390cac7b..37fbf125f 100644
--- a/src/Root.cpp
+++ b/src/Root.cpp
@@ -182,7 +182,7 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
LoadWorlds(*settingsRepo, IsNewIniFile);
LOGD("Loading plugin manager...");
- m_PluginManager = new cPluginManager();
+ m_PluginManager = new cPluginManager(dd);
m_PluginManager->ReloadPluginsNow(*settingsRepo);
LOGD("Loading MonsterConfig...");
@@ -193,7 +193,7 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
m_Authenticator.Start(*settingsRepo);
LOGD("Starting worlds...");
- StartWorlds();
+ StartWorlds(dd);
if (settingsRepo->GetValueSetB("DeadlockDetect", "Enabled", true))
{
@@ -248,7 +248,7 @@ void cRoot::Start(std::unique_ptr<cSettingsRepositoryInterface> a_OverridesRepo)
dd.Stop();
LOGD("Stopping world threads...");
- StopWorlds();
+ StopWorlds(dd);
LOGD("Stopping authenticator...");
m_Authenticator.Stop();
@@ -486,11 +486,11 @@ void cRoot::LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIn
-void cRoot::StartWorlds(void)
+void cRoot::StartWorlds(cDeadlockDetect & a_DeadlockDetect)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
{
- itr->second->Start();
+ itr->second->Start(a_DeadlockDetect);
itr->second->InitializeSpawn();
m_PluginManager->CallHookWorldStarted(*itr->second);
}
@@ -500,11 +500,11 @@ void cRoot::StartWorlds(void)
-void cRoot::StopWorlds(void)
+void cRoot::StopWorlds(cDeadlockDetect & a_DeadlockDetect)
{
for (WorldMap::iterator itr = m_WorldsByName.begin(); itr != m_WorldsByName.end(); ++itr)
{
- itr->second->Stop();
+ itr->second->Stop(a_DeadlockDetect);
}
}
diff --git a/src/Root.h b/src/Root.h
index 722554332..10848ea3f 100644
--- a/src/Root.h
+++ b/src/Root.h
@@ -27,6 +27,7 @@ class cPlayer;
class cCommandOutputCallback;
class cCompositeChat;
class cSettingsRepositoryInterface;
+class cDeadlockDetect;
typedef cItemCallback<cPlayer> cPlayerListCallback;
typedef cItemCallback<cWorld> cWorldListCallback;
@@ -226,10 +227,10 @@ private:
void LoadWorlds(cSettingsRepositoryInterface & a_Settings, bool a_IsNewIniFile);
/** Starts each world's life */
- void StartWorlds(void);
+ void StartWorlds(cDeadlockDetect & a_DeadlockDetect);
/** Stops each world's threads, so that it's safe to unload them */
- void StopWorlds(void);
+ void StopWorlds(cDeadlockDetect & a_DeadlockDetect);
/** Unloads all worlds from memory */
void UnloadWorlds(void);
diff --git a/src/World.cpp b/src/World.cpp
index a9f9bfbed..d9384b8a3 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -12,6 +12,7 @@
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
#include "SetChunkData.h"
+#include "DeadlockDetect.h"
// Serializers
#include "WorldStorage/ScoreboardSerializer.h"
@@ -433,8 +434,13 @@ void cWorld::InitializeSpawn(void)
-void cWorld::Start(void)
+void cWorld::Start(cDeadlockDetect & a_DeadlockDetect)
{
+ // Track the CSs used by this world in the deadlock detector:
+ a_DeadlockDetect.TrackCriticalSection(m_CSClients, Printf("World %s clients", m_WorldName.c_str()));
+ a_DeadlockDetect.TrackCriticalSection(m_CSPlayers, Printf("World %s players", m_WorldName.c_str()));
+ a_DeadlockDetect.TrackCriticalSection(m_CSTasks, Printf("World %s tasks", m_WorldName.c_str()));
+
m_SpawnX = 0;
m_SpawnY = cChunkDef::Height;
m_SpawnZ = 0;
@@ -603,6 +609,7 @@ void cWorld::Start(void)
SetTimeOfDay(IniFile.GetValueSetI("General", "TimeInTicks", GetTimeOfDay()));
m_ChunkMap = cpp14::make_unique<cChunkMap>(this);
+ m_ChunkMap->TrackInDeadlockDetect(a_DeadlockDetect, m_WorldName);
// preallocate some memory for ticking blocks so we don't need to allocate that often
m_BlockTickQueue.reserve(1000);
@@ -953,7 +960,7 @@ void cWorld::InitialiseAndLoadMobSpawningValues(cIniFile & a_IniFile)
-void cWorld::Stop(void)
+void cWorld::Stop(cDeadlockDetect & a_DeadlockDetect)
{
// Delete the clients that have been in this world:
{
@@ -990,6 +997,11 @@ void cWorld::Stop(void)
m_Generator.Stop();
m_ChunkSender.Stop();
m_Storage.Stop();
+
+ a_DeadlockDetect.UntrackCriticalSection(m_CSClients);
+ a_DeadlockDetect.UntrackCriticalSection(m_CSPlayers);
+ a_DeadlockDetect.UntrackCriticalSection(m_CSTasks);
+ m_ChunkMap->UntrackInDeadlockDetect(a_DeadlockDetect);
}
diff --git a/src/World.h b/src/World.h
index 707817fd4..591b61911 100644
--- a/src/World.h
+++ b/src/World.h
@@ -58,7 +58,7 @@ class cCompositeChat;
class cCuboid;
class cSetChunkData;
class cBroadcaster;
-
+class cDeadlockDetect;
typedef std::list< cPlayer * > cPlayerList;
typedef std::list< std::pair< cPlayer *, cWorld * > > cAwaitingPlayerList;
@@ -703,11 +703,13 @@ public:
void InitializeSpawn(void);
- /** Starts threads that belong to this world */
- void Start(void);
+ /** Starts threads that belong to this world.
+ a_DeadlockDetect is used for tracking this world's age, detecting a possible deadlock. */
+ void Start(cDeadlockDetect & a_DeadlockDetect);
- /** Stops threads that belong to this world (part of deinit) */
- void Stop(void);
+ /** Stops threads that belong to this world (part of deinit).
+ a_DeadlockDetect is used for tracking this world's age, detecting a possible deadlock. */
+ void Stop(cDeadlockDetect & a_DeadlockDetect);
/** Processes the blocks queued for ticking with a delay (m_BlockTickQueue[]) */
void TickQueuedBlocks(void);
diff --git a/tests/LoadablePieces/Stubs.cpp b/tests/LoadablePieces/Stubs.cpp
index 717b5679c..0ad3eb365 100644
--- a/tests/LoadablePieces/Stubs.cpp
+++ b/tests/LoadablePieces/Stubs.cpp
@@ -13,6 +13,7 @@
#include "BlockEntities/BlockEntity.h"
#include "Blocks/BlockHandler.h"
#include "Generating/ChunkDesc.h"
+#include "DeadlockDetect.h"
@@ -271,3 +272,19 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
+
+void cDeadlockDetect::TrackCriticalSection(cCriticalSection & a_CS, const AString & a_Name)
+{
+}
+
+
+
+
+
+void cDeadlockDetect::UntrackCriticalSection(cCriticalSection & a_CS)
+{
+}
+
+
+
+
diff --git a/tests/LuaThreadStress/Stubs.cpp b/tests/LuaThreadStress/Stubs.cpp
index 717b5679c..0ad3eb365 100644
--- a/tests/LuaThreadStress/Stubs.cpp
+++ b/tests/LuaThreadStress/Stubs.cpp
@@ -13,6 +13,7 @@
#include "BlockEntities/BlockEntity.h"
#include "Blocks/BlockHandler.h"
#include "Generating/ChunkDesc.h"
+#include "DeadlockDetect.h"
@@ -271,3 +272,19 @@ cBlockEntity * cBlockEntity::CreateByBlockType(BLOCKTYPE a_BlockType, NIBBLETYPE
+
+void cDeadlockDetect::TrackCriticalSection(cCriticalSection & a_CS, const AString & a_Name)
+{
+}
+
+
+
+
+
+void cDeadlockDetect::UntrackCriticalSection(cCriticalSection & a_CS)
+{
+}
+
+
+
+