Cuberite
A lightweight, fast and extensible game server for Minecraft
DeadlockDetect.cpp
Go to the documentation of this file.
1 
2 // DeadlockDetect.cpp
3 
4 // Declares the cDeadlockDetect class that tries to detect deadlocks and aborts the server when it detects one
5 
6 #include "Globals.h"
7 #include "DeadlockDetect.h"
8 #include "Root.h"
9 #include "World.h"
10 #include <cstdlib>
11 
12 
13 
14 
15 
17 const int CYCLE_MILLISECONDS = 100;
18 
19 
20 
21 
22 
24  Super("Deadlock Detector"),
25  m_IntervalSec(1000)
26 {
27 }
28 
29 
30 
31 
32 
34 {
35  // Check that all tracked CSs have been removed, report any remaining:
36  cCSLock lock(m_CS);
37  if (!m_TrackedCriticalSections.empty())
38  {
39  LOGWARNING("DeadlockDetect: Some CS objects (%u) haven't been removed from tracking", static_cast<unsigned>(m_TrackedCriticalSections.size()));
40  for (const auto & tcs: m_TrackedCriticalSections)
41  {
42  LOGWARNING(" CS %p / %s",
43  static_cast<void *>(tcs.first),
44  tcs.second.c_str()
45  );
46  }
47  }
48 }
49 
50 
51 
52 
53 
54 void cDeadlockDetect::Start(int a_IntervalSec)
55 {
56  m_IntervalSec = a_IntervalSec;
57 
58  // Read the initial world data:
59  cRoot::Get()->ForEachWorld([=](cWorld & a_World)
60  {
61  SetWorldAge(a_World.GetName(), a_World.GetWorldAge());
62  return false;
63  });
64 
65  Super::Start();
66 }
67 
68 
69 
70 
71 
73 {
74  cCSLock lock(m_CS);
75  m_TrackedCriticalSections.emplace_back(&a_CS, a_Name);
76 }
77 
78 
79 
80 
81 
83 {
84  cCSLock lock(m_CS);
85  for (auto itr = m_TrackedCriticalSections.begin(), end = m_TrackedCriticalSections.end(); itr != end; ++itr)
86  {
87  if (itr->first == &a_CS)
88  {
89  m_TrackedCriticalSections.erase(itr);
90  return;
91  }
92  }
93 }
94 
95 
96 
97 
98 
100 {
101  // Loop until the signal to terminate:
102  while (!m_ShouldTerminate)
103  {
104  // Check the world ages:
105  cRoot::Get()->ForEachWorld([=](cWorld & a_World)
106  {
107  CheckWorldAge(a_World.GetName(), a_World.GetWorldAge());
108  return false;
109  });
110 
111  std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS));
112  } // while (should run)
113 }
114 
115 
116 
117 
118 
119 void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, const cTickTimeLong a_Age)
120 {
121  m_WorldAges[a_WorldName].m_Age = a_Age;
122  m_WorldAges[a_WorldName].m_NumCyclesSame = 0;
123 }
124 
125 
126 
127 
128 
129 void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, const cTickTimeLong a_Age)
130 {
131  WorldAges::iterator itr = m_WorldAges.find(a_WorldName);
132  if (itr == m_WorldAges.end())
133  {
134  SetWorldAge(a_WorldName, a_Age);
135  return;
136  }
137 
138  cDeadlockDetect::sWorldAge & WorldAge = itr->second;
139 
140  if (WorldAge.m_Age == a_Age)
141  {
142  WorldAge.m_NumCyclesSame += 1;
143  if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS)
144  {
145  DeadlockDetected(a_WorldName, a_Age);
146  }
147  }
148  else
149  {
150  WorldAge.m_Age = a_Age;
151  WorldAge.m_NumCyclesSame = 0;
152  }
153 }
154 
155 
156 
157 
158 
159 void cDeadlockDetect::DeadlockDetected(const AString & a_WorldName, const cTickTimeLong a_WorldAge)
160 {
161  LOGERROR("Deadlock detected: world %s has been stuck at age %lld. Aborting the server.",
162  a_WorldName.c_str(), static_cast<long long>(a_WorldAge.count())
163  );
164  ListTrackedCSs();
165  ASSERT(!"Deadlock detected");
166  std::abort();
167 }
168 
169 
170 
171 
172 
174 {
175  cCSLock lock(m_CS);
176  for (const auto & cs: m_TrackedCriticalSections)
177  {
178  LOG("CS at %p, %s: RecursionCount = %d, ThreadIDHash = %04llx",
179  static_cast<void *>(cs.first), cs.second.c_str(),
180  cs.first->m_RecursionCount, static_cast<UInt64>(std::hash<std::thread::id>()(cs.first->m_OwningThreadID))
181  );
182  }
183 }
const int CYCLE_MILLISECONDS
Number of milliseconds per cycle.
std::chrono::duration< signed long long int, cTickTime::period > cTickTimeLong
Definition: Globals.h:367
unsigned long long UInt64
Definition: Globals.h:156
#define ASSERT(x)
Definition: Globals.h:276
void LOGERROR(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:73
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
std::string AString
Definition: StringUtils.h:11
void UntrackCriticalSection(cCriticalSection &a_CS)
Removes the CS from the tracking.
void CheckWorldAge(const AString &a_WorldName, cTickTimeLong a_Age)
Checks if the world's age has changed, updates the world's stats; calls DeadlockDetected() if deadloc...
int m_IntervalSec
Number of secods for which the ages must be the same for the detection to trigger.
void ListTrackedCSs()
Outputs a listing of the tracked CSs, together with their name and state.
WorldAges m_WorldAges
virtual ~cDeadlockDetect() override
cCriticalSection m_CS
Protects m_TrackedCriticalSections from multithreaded access.
void SetWorldAge(const AString &a_WorldName, cTickTimeLong a_Age)
Sets the initial world age.
void DeadlockDetected(const AString &a_WorldName, cTickTimeLong a_WorldAge)
Called when a deadlock is detected in a world.
void TrackCriticalSection(cCriticalSection &a_CS, const AString &a_Name)
Adds the critical section for tracking.
std::vector< std::pair< cCriticalSection *, AString > > m_TrackedCriticalSections
CriticalSections that are tracked (their status output on deadlock).
virtual void Execute(void) override
This function, overloaded by the descendants, is called in the new thread.
cTickTimeLong m_Age
Last m_WorldAge that has been detected in this world.
int m_NumCyclesSame
Number of cycles for which the age has been the same.
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true.
Definition: IsThread.h:45
void Start(void)
Starts the thread; returns without waiting for the actual start.
Definition: IsThread.cpp:35
static cRoot * Get()
Definition: Root.h:52
bool ForEachWorld(cWorldListCallback a_Callback)
Calls the callback for each world; returns true if the callback didn't abort (return true)
Definition: Root.cpp:480
Definition: World.h:53
virtual cTickTimeLong GetWorldAge(void) const override
Definition: World.cpp:491
const AString & GetName(void) const
Returns the name of the world.
Definition: World.h:691