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("DeadlockDetect"),
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 bool 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  return super::Start();
66 }
67 
68 
69 
70 
71 
73 {
74  cCSLock lock(m_CS);
75  m_TrackedCriticalSections.emplace_back(std::make_pair(&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 
112  std::this_thread::sleep_for(std::chrono::milliseconds(CYCLE_MILLISECONDS));
113  } // while (should run)
114 }
115 
116 
117 
118 
119 
120 void cDeadlockDetect::SetWorldAge(const AString & a_WorldName, Int64 a_Age)
121 {
122  m_WorldAges[a_WorldName].m_Age = a_Age;
123  m_WorldAges[a_WorldName].m_NumCyclesSame = 0;
124 }
125 
126 
127 
128 
129 
130 void cDeadlockDetect::CheckWorldAge(const AString & a_WorldName, Int64 a_Age)
131 {
132  WorldAges::iterator itr = m_WorldAges.find(a_WorldName);
133  if (itr == m_WorldAges.end())
134  {
135  SetWorldAge(a_WorldName, a_Age);
136  return;
137  }
138 
139  cDeadlockDetect::sWorldAge & WorldAge = itr->second;
140 
141  if (WorldAge.m_Age == a_Age)
142  {
143  WorldAge.m_NumCyclesSame += 1;
144  if (WorldAge.m_NumCyclesSame > (m_IntervalSec * 1000) / CYCLE_MILLISECONDS)
145  {
146  DeadlockDetected(a_WorldName, a_Age);
147  }
148  }
149  else
150  {
151  WorldAge.m_Age = a_Age;
152  WorldAge.m_NumCyclesSame = 0;
153  }
154 }
155 
156 
157 
158 
159 
160 void cDeadlockDetect::DeadlockDetected(const AString & a_WorldName, Int64 a_WorldAge)
161 {
162  LOGERROR("Deadlock detected: world %s has been stuck at age %lld. Aborting the server.",
163  a_WorldName.c_str(), static_cast<long long>(a_WorldAge)
164  );
165  ListTrackedCSs();
166  ASSERT(!"Deadlock detected");
167  abort();
168 }
169 
170 
171 
172 
173 
175 {
176  cCSLock lock(m_CS);
177  for (const auto & cs: m_TrackedCriticalSections)
178  {
179  LOG("CS at %p, %s: RecursionCount = %d, ThreadIDHash = %04llx",
180  static_cast<void *>(cs.first), cs.second.c_str(),
181  cs.first->m_RecursionCount, static_cast<UInt64>(std::hash<std::thread::id>()(cs.first->m_OwningThreadID))
182  );
183  }
184 }
185 
186 
187 
188 
virtual void Execute(void) override
This is the main thread entrypoint.
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true...
Definition: IsThread.h:32
WorldAges m_WorldAges
void CheckWorldAge(const AString &a_WorldName, Int64 a_Age)
Checks if the world&#39;s age has changed, updates the world&#39;s stats; calls DeadlockDetected() if deadloc...
bool ForEachWorld(cWorldListCallback a_Callback)
Calls the callback for each world; returns true if the callback didn&#39;t abort (return true) ...
Definition: Root.cpp:656
void SetWorldAge(const AString &a_WorldName, Int64 a_Age)
Sets the initial world age.
const int CYCLE_MILLISECONDS
Number of milliseconds per cycle.
void LOGERROR(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:183
NORETURN void DeadlockDetected(const AString &a_WorldName, Int64 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.
Definition: World.h:65
int m_IntervalSec
Number of secods for which the ages must be the same for the detection to trigger.
virtual ~cDeadlockDetect() override
void UntrackCriticalSection(cCriticalSection &a_CS)
Removes the CS from the tracking.
unsigned long long UInt64
Definition: Globals.h:112
virtual Int64 GetWorldAge(void) const override
Definition: World.h:109
#define ASSERT(x)
Definition: Globals.h:335
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
std::string AString
Definition: StringUtils.h:13
const AString & GetName(void) const
Returns the name of the world.
Definition: World.h:874
cCriticalSection m_CS
Protects m_TrackedCriticalSections from multithreaded access.
void ListTrackedCSs()
Outputs a listing of the tracked CSs, together with their name and state.
static cRoot * Get()
Definition: Root.h:51
void LOG(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:156
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
std::vector< std::pair< cCriticalSection *, AString > > m_TrackedCriticalSections
CriticalSections that are tracked (their status output on deadlock).
int m_NumCyclesSame
Number of cycles for which the age has been the same.
Int64 m_Age
Last m_WorldAge that has been detected in this world.
signed long long Int64
Definition: Globals.h:107
bool Start(void)
Starts the thread; returns without waiting for the actual start.
Definition: IsThread.cpp:80