Crashlog: Update thread/lock handling to match GameLoop changes

pull/238/head
Jonathan G Rennison 3 years ago
parent d452517c66
commit 63ddf7f587

@ -78,7 +78,6 @@
/* static */ const char *CrashLog::message = nullptr; /* static */ const char *CrashLog::message = nullptr;
/* static */ char *CrashLog::gamelog_buffer = nullptr; /* static */ char *CrashLog::gamelog_buffer = nullptr;
/* static */ const char *CrashLog::gamelog_last = nullptr; /* static */ const char *CrashLog::gamelog_last = nullptr;
/* static */ const CrashLog *CrashLog::main_thread_pending_crashlog = nullptr;
char *CrashLog::LogCompiler(char *buffer, const char *last) const char *CrashLog::LogCompiler(char *buffer, const char *last) const
{ {
@ -441,7 +440,7 @@ char *CrashLog::FillCrashLog(char *buffer, const char *last) const
buffer = this->LogError(buffer, last, CrashLog::message); buffer = this->LogError(buffer, last, CrashLog::message);
#ifdef USE_SCOPE_INFO #ifdef USE_SCOPE_INFO
if (IsMainThread()) { if (IsMainThread() || IsGameThread()) {
buffer += WriteScopeLog(buffer, last); buffer += WriteScopeLog(buffer, last);
} }
#endif #endif
@ -644,6 +643,10 @@ bool CrashLog::MakeCrashLog()
if (crashlogged) return false; if (crashlogged) return false;
crashlogged = true; crashlogged = true;
if (!VideoDriver::EmergencyAcquireGameLock(20, 2)) {
printf("Failed to acquire gamelock before filling crash log\n\n");
}
char filename[MAX_PATH]; char filename[MAX_PATH];
char buffer[65536 * 4]; char buffer[65536 * 4];
bool ret = true; bool ret = true;
@ -679,15 +682,10 @@ bool CrashLog::MakeCrashLog()
_savegame_DBGL_data = buffer; _savegame_DBGL_data = buffer;
_save_DBGC_data = true; _save_DBGC_data = true;
if (IsNonMainThread()) { if (!VideoDriver::EmergencyAcquireGameLock(1000, 5)) {
printf("Asking main thread to write crash savegame and screenshot...\n\n"); printf("Failed to acquire gamelock before writing crash savegame and screenshot, proceeding without lock as current owner is probably stuck\n\n");
CrashLog::main_thread_pending_crashlog = this;
_exit_game = true;
CSleep(60000);
if (!CrashLog::main_thread_pending_crashlog) return ret;
printf("Main thread did not write crash savegame and screenshot within 60s, trying it from this thread...\n\n");
} }
CrashLog::main_thread_pending_crashlog = nullptr;
bret = CrashLog::MakeCrashSavegameAndScreenshot(); bret = CrashLog::MakeCrashSavegameAndScreenshot();
if (!bret) ret = false; if (!bret) ret = false;
@ -802,18 +800,6 @@ bool CrashLog::MakeCrashSavegameAndScreenshot() const
return ret; return ret;
} }
/* static */ void CrashLog::MainThreadExitCheckPendingCrashlog()
{
const CrashLog *cl = CrashLog::main_thread_pending_crashlog;
if (cl) {
CrashLog::main_thread_pending_crashlog = nullptr;
cl->MakeCrashSavegameAndScreenshot();
CrashLog::AfterCrashLogCleanup();
abort();
}
}
/** /**
* Sets a message for the error message handler. * Sets a message for the error message handler.
* @param message The error message of the error. * @param message The error message of the error.

@ -173,10 +173,6 @@ public:
inline const char *GetMessage() const { return this->message; } inline const char *GetMessage() const { return this->message; }
static const char *GetAbortCrashlogReason(); static const char *GetAbortCrashlogReason();
static const CrashLog *main_thread_pending_crashlog;
static void MainThreadExitCheckPendingCrashlog();
}; };
#endif /* CRASHLOG_H */ #endif /* CRASHLOG_H */

@ -997,8 +997,6 @@ int openttd_main(int argc, char *argv[])
VideoDriver::GetInstance()->MainLoop(); VideoDriver::GetInstance()->MainLoop();
CrashLog::MainThreadExitCheckPendingCrashlog();
WaitTillSaved(); WaitTillSaved();
/* only save config if we have to */ /* only save config if we have to */

@ -219,8 +219,10 @@ void SetCurrentThreadName(const char *)
int GetCurrentThreadName(char *str, const char *last) { return 0; } int GetCurrentThreadName(char *str, const char *last) { return 0; }
void SetSelfAsMainThread() { } void SetSelfAsMainThread() { }
void SetSelfAsGameThread() { }
void PerThreadSetup() { } void PerThreadSetup() { }
void PerThreadSetupInit() { } void PerThreadSetupInit() { }
bool IsMainThread() { return false; } bool IsMainThread() { return false; }
bool IsNonMainThread() { return false; } bool IsNonMainThread() { return false; }
bool IsGameThread() { return false; }

@ -341,6 +341,7 @@ int GetCurrentThreadName(char *str, const char *last)
} }
static pthread_t main_thread; static pthread_t main_thread;
static pthread_t game_thread;
void SetSelfAsMainThread() void SetSelfAsMainThread()
{ {
@ -349,6 +350,13 @@ void SetSelfAsMainThread()
#endif #endif
} }
void SetSelfAsGameThread()
{
#if !defined(NO_THREADS)
game_thread = pthread_self();
#endif
}
void PerThreadSetup() { } void PerThreadSetup() { }
void PerThreadSetupInit() { } void PerThreadSetupInit() { }
@ -370,3 +378,12 @@ bool IsNonMainThread()
return false; return false;
#endif #endif
} }
bool IsGameThread()
{
#if !defined(NO_THREADS)
return game_thread == pthread_self();
#else
return false;
#endif
}

@ -639,6 +639,8 @@ static LONG WINAPI ExceptionHandler(EXCEPTION_POINTERS *ep)
ExitProcess(3); ExitProcess(3);
} }
VideoDriver::EmergencyAcquireGameLock(1000, 5);
CrashLogWindows *log = new CrashLogWindows(ep); CrashLogWindows *log = new CrashLogWindows(ep);
CrashLogWindows::current = log; CrashLogWindows::current = log;
char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog)); char *buf = log->FillCrashLog(log->crashlog, lastof(log->crashlog));

@ -734,12 +734,18 @@ int OTTDStringCompare(const char *s1, const char *s2)
} }
static DWORD main_thread_id; static DWORD main_thread_id;
static DWORD game_thread_id;
void SetSelfAsMainThread() void SetSelfAsMainThread()
{ {
main_thread_id = GetCurrentThreadId(); main_thread_id = GetCurrentThreadId();
} }
void SetSelfAsGameThread()
{
game_thread_id = GetCurrentThreadId();
}
static BOOL (WINAPI *_SetThreadStackGuarantee)(PULONG) = nullptr; static BOOL (WINAPI *_SetThreadStackGuarantee)(PULONG) = nullptr;
void PerThreadSetup() void PerThreadSetup()
@ -765,6 +771,11 @@ bool IsNonMainThread()
return main_thread_id != GetCurrentThreadId(); return main_thread_id != GetCurrentThreadId();
} }
bool IsGameThread()
{
return game_thread_id == GetCurrentThreadId();
}
static std::map<DWORD, std::string> _thread_name_map; static std::map<DWORD, std::string> _thread_name_map;
static std::mutex _thread_name_map_mutex; static std::mutex _thread_name_map_mutex;

@ -46,6 +46,11 @@ int GetCurrentThreadName(char *str, const char *last);
*/ */
void SetSelfAsMainThread(); void SetSelfAsMainThread();
/**
* Set the current thread as the "game" thread
*/
void SetSelfAsGameThread();
/** /**
* Perform per-thread setup * Perform per-thread setup
*/ */
@ -57,15 +62,20 @@ void PerThreadSetup();
void PerThreadSetupInit(); void PerThreadSetupInit();
/** /**
* @return true if the current thread definitely the "main" thread. If in doubt returns false. * @return true if the current thread is definitely the "main" thread. If in doubt returns false.
*/ */
bool IsMainThread(); bool IsMainThread();
/** /**
* @return true if the current thread definitely a "non-main" thread. If in doubt returns false. * @return true if the current thread is definitely a "non-main" thread. If in doubt returns false.
*/ */
bool IsNonMainThread(); bool IsNonMainThread();
/**
* @return true if the current thread is definitely the "game" thread. If in doubt returns false.
*/
bool IsGameThread();
/** /**
* Start a new thread. * Start a new thread.

@ -31,7 +31,7 @@ void VideoDriver::GameLoop()
if (this->next_game_tick < now - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = now; if (this->next_game_tick < now - ALLOWED_DRIFT * this->GetGameInterval()) this->next_game_tick = now;
{ {
std::lock_guard<std::mutex> lock(this->game_state_mutex); std::lock_guard<std::recursive_mutex> lock(this->game_state_mutex);
::GameLoop(); ::GameLoop();
} }
@ -75,8 +75,23 @@ void VideoDriver::GameLoopPause()
this->game_state_mutex.lock(); this->game_state_mutex.lock();
} }
/* static */ bool VideoDriver::EmergencyAcquireGameLock(uint tries, uint delay_ms)
{
VideoDriver *drv = VideoDriver::GetInstance();
if (drv == nullptr) return true;
for (uint i = 0; i < tries; i++) {
if (drv->game_state_mutex.try_lock()) return true;
CSleep(delay_ms);
}
return false;
}
/* static */ void VideoDriver::GameThreadThunk(VideoDriver *drv) /* static */ void VideoDriver::GameThreadThunk(VideoDriver *drv)
{ {
SetSelfAsGameThread();
drv->GameThread(); drv->GameThread();
} }
@ -135,7 +150,7 @@ void VideoDriver::Tick()
{ {
/* Tell the game-thread to stop so we can have a go. */ /* Tell the game-thread to stop so we can have a go. */
std::lock_guard<std::mutex> lock_wait(this->game_thread_wait_mutex); std::lock_guard<std::mutex> lock_wait(this->game_thread_wait_mutex);
std::lock_guard<std::mutex> lock_state(this->game_state_mutex); std::lock_guard<std::recursive_mutex> lock_state(this->game_state_mutex);
this->next_draw_tick += this->GetDrawInterval(); this->next_draw_tick += this->GetDrawInterval();
/* Avoid next_draw_tick getting behind more and more if it cannot keep up. */ /* Avoid next_draw_tick getting behind more and more if it cannot keep up. */

@ -212,6 +212,8 @@ public:
bool unlock; ///< Stores if the lock did anything that has to be undone. bool unlock; ///< Stores if the lock did anything that has to be undone.
}; };
static bool EmergencyAcquireGameLock(uint tries, uint delay_ms);
protected: protected:
const uint ALLOWED_DRIFT = 5; ///< How many times videodriver can miss deadlines without it being overly compensated. const uint ALLOWED_DRIFT = 5; ///< How many times videodriver can miss deadlines without it being overly compensated.
@ -322,7 +324,7 @@ protected:
bool is_game_threaded; bool is_game_threaded;
std::thread game_thread; std::thread game_thread;
std::mutex game_state_mutex; std::recursive_mutex game_state_mutex;
std::mutex game_thread_wait_mutex; std::mutex game_thread_wait_mutex;
static void GameThreadThunk(VideoDriver *drv); static void GameThreadThunk(VideoDriver *drv);

Loading…
Cancel
Save