示例#1
0
void GameSetupDrawer::StartCountdown(unsigned time)
{
    if (instance) {
        instance->lastTick = spring_gettime(); //FIXME
        instance->readyCountdown = spring_msecs(time);
    }
}
示例#2
0
void HangDetector() {
	while (keepRunning) {
		// increase multiplier during game load to prevent false positives e.g. during pathing
		const int hangTimeMultiplier = CrashHandler::gameLoading? 3 : 1;
		const spring_time hangtimeout = spring_msecs(spring_tomsecs(hangTimeout) * hangTimeMultiplier);

		spring_time curwdt = spring_gettime();
#if defined(USE_GML) && GML_ENABLE_SIM
		if (gmlMultiThreadSim) {
			spring_time cursimwdt = simwdt;
			if (spring_istime(cursimwdt) && curwdt > cursimwdt && (curwdt - cursimwdt) > hangtimeout) {
				HangHandler(true);
				simwdt = curwdt;
			}
		}
#endif
		spring_time curdrawwdt = drawwdt;
		if (spring_istime(curdrawwdt) && curwdt > curdrawwdt && (curwdt - curdrawwdt) > hangtimeout) {
			HangHandler(false);
			drawwdt = curwdt;
		}

		spring_sleep(spring_secs(1));
	}
}
示例#3
0
__FORCE_ALIGN_STACK__
void CNetProtocol::UpdateLoop()
{
	Threading::SetThreadName("heartbeat");

	while (keepUpdating) {
		Update();
		spring_msecs(100).sleep();
	}
}
示例#4
0
void spring_time::Serialize(creg::ISerializer& s)
{
    if (s.IsWriting()) {
        int y = spring_tomsecs(*this - spring_gettime());
        s.SerializeInt(&y, 4);
    } else {
        int y;
        s.SerializeInt(&y, 4);
        *this = *this + spring_msecs(y);
    }
}
示例#5
0
void GameParticipant::Kill(const std::string& reason, const bool flush)
{
	if (link)
	{
		link->SendData(CBaseNetProtocol::Get().SendQuit(reason));
		if (flush) // make sure the Flush() performed by Close() has any effect (forced flushes are undesirable)
			spring_sleep(spring_msecs(1000)); // it will cause a slight lag in the game server during kick, but not a big deal
		link->Close();
		link.reset();
	}
	linkData[MAX_AIS].link.reset();
#ifdef SYNCCHECK
	syncResponse.clear();
#endif
	myState = DISCONNECTED;
}
示例#6
0
void BenchmarkSleepFnc(const std::string& name, void (*sleep)(int time), const int runs, const float toMilliSecondsScale)
{
	// waste a few cycles to push the cpu to higher frequency states
	for (auto spinStopTime = spring_gettime() + spring_secs(2); spring_gettime() < spinStopTime; ) {
	}

	spring_time t = spring_gettime();
	spring_time tmin, tmax;
	float tavg = 0;

	// check lowest possible sleep tick
	for (int i=0; i<runs; ++i) {
		sleep(0);

		spring_time diff = spring_gettime() - t;
		if ((diff > tmax) || !spring_istime(tmax)) tmax = diff;
		if ((diff < tmin) || !spring_istime(tmin)) tmin = diff;
		tavg = float(i * tavg + diff.toNanoSecsi()) / (i + 1);
		t = spring_gettime();
	}

	// check error in sleeping times
	spring_time emin, emax;
	float eavg = 0;
	if (toMilliSecondsScale != 0) {
		for (int i=0; i<100; ++i) {
			const auto sleepTime = (rand() % 50) * 0.1f + 2; // 2..7ms

			t = spring_gettime();
			sleep(sleepTime * toMilliSecondsScale);
			spring_time diff = (spring_gettime() - t) - spring_msecs(sleepTime);

			if ((diff > emax) || !emax.isDuration()) emax = diff;
			if ((diff < emin) || !emin.isDuration()) emin = diff;
			eavg = float(i * eavg + std::abs(diff.toNanoSecsf())) / (i + 1);
		}
	}

	LOG("[%35s] accuracy:={ err: %+.4fms %+.4fms erravg: %.4fms } min sleep time:={ min: %.6fms avg: %.6fms max: %.6fms }", name.c_str(), emin.toMilliSecsf(), emax.toMilliSecsf(), eavg * 1e-6, tmin.toMilliSecsf(), tavg * 1e-6, tmax.toMilliSecsf());
}
示例#7
0
bool CLoadScreen::Draw()
{
    //! Limit the Frames Per Second to not lock a singlethreaded CPU from loading the game
    if (mtLoading) {
        spring_time now = spring_gettime();
        unsigned diff_ms = spring_tomsecs(now - last_draw);
        static const unsigned wantedFPS = 50;
        static const unsigned min_frame_time = 1000 / wantedFPS;
        if (diff_ms < min_frame_time) {
            spring_time nap = spring_msecs(min_frame_time - diff_ms);
            spring_sleep(nap);
        }
        last_draw = now;
    }

    //! cause of `curLoadMessage`
    boost::recursive_mutex::scoped_lock lck(mutex);

    if (LuaIntro != nullptr) {
        LuaIntro->Update();
        LuaIntro->DrawGenesis();
        ClearScreen();
        LuaIntro->DrawLoadScreen();
    } else {
        ClearScreen();

        float xDiv = 0.0f;
        float yDiv = 0.0f;
        const float ratioComp = globalRendering->aspectRatio / aspectRatio;
        if (math::fabs(ratioComp - 1.0f) < 0.01f) { //! ~= 1
            //! show Load-Screen full screen
            //! nothing to do
        } else if (ratioComp > 1.0f) {
            //! show Load-Screen on part of the screens X-Axis only
            xDiv = (1.0f - (1.0f / ratioComp)) * 0.5f;
        } else {
            //! show Load-Screen on part of the screens Y-Axis only
            yDiv = (1.0f - ratioComp) * 0.5f;
        }

        //! Draw loading screen & print load msg.
        if (startupTexture) {
            glBindTexture(GL_TEXTURE_2D,startupTexture);
            glBegin(GL_QUADS);
            glTexCoord2f(0.0f, 1.0f);
            glVertex2f(0.0f + xDiv, 0.0f + yDiv);
            glTexCoord2f(0.0f, 0.0f);
            glVertex2f(0.0f + xDiv, 1.0f - yDiv);
            glTexCoord2f(1.0f, 0.0f);
            glVertex2f(1.0f - xDiv, 1.0f - yDiv);
            glTexCoord2f(1.0f, 1.0f);
            glVertex2f(1.0f - xDiv, 0.0f + yDiv);
            glEnd();
        }

        if (showMessages) {
            font->Begin();
            font->SetTextColor(0.5f,0.5f,0.5f,0.9f);
            font->glPrint(0.1f,0.9f,   globalRendering->viewSizeY / 35.0f, FONT_NORM,
                          oldLoadMessages);

            font->SetTextColor(0.9f,0.9f,0.9f,0.9f);
            float posy = font->GetTextNumLines(oldLoadMessages) * font->GetLineHeight() * globalRendering->viewSizeY / 35.0f;
            font->glPrint(0.1f,0.9f - posy * globalRendering->pixelY,   globalRendering->viewSizeY / 35.0f, FONT_NORM,
                          curLoadMessage);
            font->End();
        }
    }

    // Always render Spring's license notice
    font->Begin();
    font->SetOutlineColor(0.0f,0.0f,0.0f,0.65f);
    font->SetTextColor(1.0f,1.0f,1.0f,1.0f);
    font->glFormat(0.5f,0.06f, globalRendering->viewSizeY / 35.0f, FONT_OUTLINE | FONT_CENTER | FONT_NORM,
                   "Spring %s", SpringVersion::GetFull().c_str());
    font->glFormat(0.5f,0.02f, globalRendering->viewSizeY / 50.0f, FONT_OUTLINE | FONT_CENTER | FONT_NORM,
                   "This program is distributed under the GNU General Public License, see license.html for more info");
    font->End();

    if (!mtLoading)
        SDL_GL_SwapWindow(globalRendering->window);

    return true;
}
示例#8
0
void CSoundSource::Play(IAudioChannel* channel, SoundItem* item, float3 pos, float3 velocity, float volume, bool relative)
{
	assert(!curStream.Valid());
	assert(channel);

	if (!item->PlayNow())
		return;

	Stop();
	curVolume = volume;
	curPlaying = item;
	curChannel = channel;
	alSourcei(id, AL_BUFFER, item->buffer->GetId());
	alSourcef(id, AL_GAIN, volume * item->GetGain() * channel->volume);
	alSourcef(id, AL_PITCH, item->GetPitch() * globalPitch);
	velocity *= item->dopplerScale * ELMOS_TO_METERS;
	alSource3f(id, AL_VELOCITY, velocity.x, velocity.y, velocity.z);
	alSourcei(id, AL_LOOPING, (item->loopTime > 0) ? AL_TRUE : AL_FALSE);
	loopStop = spring_gettime() + spring_msecs(item->loopTime);
	if (relative || !item->in3D) {
		in3D = false;
		if (efxEnabled) {
			alSource3i(id, AL_AUXILIARY_SEND_FILTER, AL_EFFECTSLOT_NULL, 0, AL_FILTER_NULL);
			alSourcei(id, AL_DIRECT_FILTER, AL_FILTER_NULL);
			efxEnabled = false;
		}
		alSourcei(id, AL_SOURCE_RELATIVE, AL_TRUE);
		alSourcef(id, AL_ROLLOFF_FACTOR, 0.f);
		alSource3f(id, AL_POSITION, 0.0f, 0.0f, -1.0f * ELMOS_TO_METERS);
#ifdef __APPLE__
		alSourcef(id, AL_REFERENCE_DISTANCE, referenceDistance * ELMOS_TO_METERS);
#endif
	} else {
		if (item->buffer->GetChannels() > 1) {
			LOG_L(L_WARNING, "Can not play non-mono \"%s\" in 3d.",
					item->buffer->GetFilename().c_str());
		}

		in3D = true;
		if (efx->enabled) {
			efxEnabled = true;
			alSourcef(id, AL_AIR_ABSORPTION_FACTOR, efx->GetAirAbsorptionFactor());
			alSource3i(id, AL_AUXILIARY_SEND_FILTER, efx->sfxSlot, 0, AL_FILTER_NULL);
			alSourcei(id, AL_DIRECT_FILTER, efx->sfxFilter);
			efxUpdates = efx->updates;
		}
		alSourcei(id, AL_SOURCE_RELATIVE, AL_FALSE);
		pos *= ELMOS_TO_METERS;
		alSource3f(id, AL_POSITION, pos.x, pos.y, pos.z);
		curHeightRolloffModifier = heightRolloffModifier;
		alSourcef(id, AL_ROLLOFF_FACTOR, ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier);
#ifdef __APPLE__
		alSourcef(id, AL_MAX_DISTANCE, 1000000.0f);
		//! Max distance is too small by default on my Mac...
		ALfloat gain = channel->volume * item->GetGain() * volume;
		if (gain > 1.0f) {
			//! OpenAL on Mac cannot handle AL_GAIN > 1 well, so we will adjust settings to get the same output with AL_GAIN = 1.
			ALint model = alGetInteger(AL_DISTANCE_MODEL);
			ALfloat rolloff = ROLLOFF_FACTOR * item->rolloff * heightRolloffModifier;
			ALfloat ref = referenceDistance * ELMOS_TO_METERS;
			if ((model == AL_INVERSE_DISTANCE_CLAMPED) || (model == AL_INVERSE_DISTANCE)) {
				alSourcef(id, AL_REFERENCE_DISTANCE, ((gain - 1.0f) * ref / rolloff) + ref);
				alSourcef(id, AL_ROLLOFF_FACTOR, (gain + rolloff - 1.0f) / gain);
				alSourcef(id, AL_GAIN, 1.0f);
			}
		} else {
			alSourcef(id, AL_REFERENCE_DISTANCE, referenceDistance * ELMOS_TO_METERS);
		}
#endif
	}
	alSourcePlay(id);

	if (item->buffer->GetId() == 0)
		LOG_L(L_WARNING, "CSoundSource::Play: Empty buffer for item %s (file %s)", item->name.c_str(), item->buffer->GetFilename().c_str());

	CheckError("CSoundSource::Play");
}
示例#9
0
namespace Watchdog
{
	const char *threadNames[] = {"main", "sim", "load", "audio"};

	static boost::mutex wdmutex;

	static unsigned int curorder = 0;

	struct WatchDogThreadInfo {
		WatchDogThreadInfo()
			: threadid(0)
			, timer(spring_notime)
			, numreg(0)
		{}
		volatile Threading::NativeThreadHandle thread;
		volatile Threading::NativeThreadId threadid;
		spring_time timer;
		volatile unsigned int numreg;
	};
	static WatchDogThreadInfo registeredThreadsData[WDT_SIZE];
	struct WatchDogThreadSlot {
		WatchDogThreadSlot()
			: primary(false)
			, active(false)
			, regorder(0)
		{}
		volatile bool primary;
		volatile bool active;
		volatile unsigned int regorder;
	};
	static WatchDogThreadInfo *registeredThreads[WDT_SIZE] = {NULL};
	static WatchDogThreadSlot threadSlots[WDT_SIZE];

	static std::map<std::string, unsigned int> threadNameToNum;

	static boost::thread* hangDetectorThread = NULL;
	static spring_time hangTimeout = spring_msecs(0);
	static volatile bool hangDetectorThreadInterrupted = false;

	static inline void UpdateActiveThreads(Threading::NativeThreadId num) {
		unsigned int active = WDT_LAST;
		for (unsigned int i = 0; i < WDT_LAST; ++i) {
			WatchDogThreadInfo* th_info = registeredThreads[i];
			if (th_info->numreg && Threading::NativeThreadIdsEqual(num, th_info->threadid)) {
				threadSlots[i].active = false;
				if (threadSlots[i].regorder > threadSlots[active].regorder)
					active = i;
			}
		}
		if (active < WDT_LAST)
			threadSlots[active].active = true;
	}


	__FORCE_ALIGN_STACK__
	static void HangDetectorLoop()
	{
		Threading::SetThreadName("watchdog");

		while (!hangDetectorThreadInterrupted) {
			spring_time curtime = spring_gettime();
			bool hangDetected = false;

			for (unsigned int i = 0; i < WDT_LAST; ++i) {
				if (!threadSlots[i].active)
					continue;

				WatchDogThreadInfo* th_info = registeredThreads[i];
				spring_time curwdt = th_info->timer;

				if (spring_istime(curwdt) && (curtime - curwdt) > hangTimeout) {
					if (!hangDetected) {
						LOG_L(L_WARNING, "[Watchdog] Hang detection triggered for Spring %s.", SpringVersion::GetFull().c_str());
						if (GML::Enabled())
							LOG_L(L_WARNING, "MT with %d threads.", GML::ThreadCount());
					}
					LOG_L(L_WARNING, "  (in thread: %s)", threadNames[i]);

					hangDetected = true;
					th_info->timer = curtime;
				}
			}

			if (hangDetected) {
				CrashHandler::PrepareStacktrace(LOG_LEVEL_WARNING);

				for (unsigned int i = 0; i < WDT_LAST; ++i) {
					if (!threadSlots[i].active)
						continue;

					WatchDogThreadInfo* th_info = registeredThreads[i];
					CrashHandler::Stacktrace(th_info->thread, threadNames[i], LOG_LEVEL_WARNING);
				}

				CrashHandler::CleanupStacktrace(LOG_LEVEL_WARNING);
			}

			boost::this_thread::sleep(boost::posix_time::seconds(1));
		}
	}


	void RegisterThread(WatchdogThreadnum num, bool primary)
	{
		boost::mutex::scoped_lock lock(wdmutex);

		if (num >= WDT_LAST || registeredThreads[num]->numreg) {
			LOG_L(L_ERROR, "[Watchdog::RegisterThread] Invalid thread number");
			return;
		}

		Threading::NativeThreadHandle thread = Threading::GetCurrentThread();
		Threading::NativeThreadId threadId = Threading::GetCurrentThreadId();

		unsigned int inact = WDT_LAST;
		unsigned int i;
		for (i = 0; i < WDT_LAST; ++i) {
			WatchDogThreadInfo* th_info = &registeredThreadsData[i];
			if (!th_info->numreg)
				inact = std::min(i, inact);
			else if(Threading::NativeThreadIdsEqual(th_info->threadid, threadId))
				break;
		}
		if (i >= WDT_LAST)
			i = inact;
		if (i >= WDT_LAST) {
			LOG_L(L_ERROR, "[Watchdog::RegisterThread] Internal error");
			return;
		}
		registeredThreads[num] = &registeredThreadsData[i];

		// set threadname
		//Threading::SetThreadName(threadNames[num]);

		WatchDogThreadInfo* th_info = registeredThreads[num];
		th_info->thread = thread;
		th_info->threadid = threadId;
		th_info->timer = spring_gettime();
		++th_info->numreg;

		threadSlots[num].primary = primary;
		threadSlots[num].regorder = ++curorder;
		UpdateActiveThreads(threadId);
	}


	void DeregisterThread(WatchdogThreadnum num)
	{
		boost::mutex::scoped_lock lock(wdmutex);

		WatchDogThreadInfo* th_info;
		if (num >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::DeregisterThread] Invalid thread number");
			return;
		}
		threadSlots[num].primary = false;
		threadSlots[num].regorder = 0;
		UpdateActiveThreads(th_info->threadid);

		if(!--(th_info->numreg))
			memset(th_info, 0, sizeof(WatchDogThreadInfo));

		registeredThreads[num] = &registeredThreadsData[WDT_LAST];
	}


	void ClearTimer(bool disable, Threading::NativeThreadId* _threadId)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		Threading::NativeThreadId threadId;
		if (_threadId)
			threadId = *_threadId;
		else
			threadId = Threading::GetCurrentThreadId();
		unsigned int num = 0;
		for (; num < WDT_LAST; ++num)
			if (Threading::NativeThreadIdsEqual(registeredThreads[num]->threadid, threadId))
				break;
		WatchDogThreadInfo* th_info;
		if (num >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::ClearTimer] Invalid thread %d", num);
			return;
		}

		th_info->timer = disable ? spring_notime : spring_gettime();
	}


	void ClearTimer(WatchdogThreadnum num, bool disable)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		WatchDogThreadInfo* th_info;
		if (num >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::ClearTimer] Invalid thread number %d", num);
			return;
		}

		th_info->timer = disable ? spring_notime : spring_gettime();
	}

	void ClearTimer(const std::string &name, bool disable)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		std::map<std::string, unsigned int>::iterator i = threadNameToNum.find(name);
		unsigned int num;
		WatchDogThreadInfo* th_info;
		if (i == threadNameToNum.end() || (num = i->second) >= WDT_LAST || !(th_info = registeredThreads[num])->numreg) {
			LOG_L(L_ERROR, "[Watchdog::ClearTimer] Invalid thread name");
			return;
		}

		th_info->timer = disable ? spring_notime : spring_gettime();
	}

	void ClearPrimaryTimers(bool disable)
	{
		if (hangDetectorThread == NULL)
			return; //! Watchdog isn't running

		for (unsigned int i = 0; i < WDT_LAST; ++i) {
			WatchDogThreadInfo* th_info = registeredThreads[i];
			if (threadSlots[i].primary)
				th_info->timer = disable ? spring_notime : spring_gettime();
		}
	}


	void Install()
	{
		boost::mutex::scoped_lock lock(wdmutex);

		memset(registeredThreadsData, 0, sizeof(registeredThreadsData));
		for (unsigned int i = 0; i < WDT_LAST; ++i) {
			registeredThreads[i] = &registeredThreadsData[WDT_LAST];
			threadNameToNum[std::string(threadNames[i])] = i;
		}
		memset(threadSlots, 0, sizeof(threadSlots));

	#ifndef _WIN32
		// disable if gdb is running
		char buf[1024];
		SNPRINTF(buf, sizeof(buf), "/proc/%d/cmdline", getppid());
		std::ifstream f(buf);
		if (f.good()) {
			f.read(buf,sizeof(buf));
			f.close();
			std::string str(buf);
			if (str.find("gdb") != std::string::npos) {
				LOG("[Watchdog] disabled (gdb detected)");
				return;
			}
		}
	#endif
	#ifdef USE_VALGRIND
		if (RUNNING_ON_VALGRIND) {
			LOG("[Watchdog] disabled (Valgrind detected)");
			return;
		}
	#endif
		const int hangTimeoutSecs = configHandler->GetInt("HangTimeout");

		// HangTimeout = -1 to force disable hang detection
		if (hangTimeoutSecs <= 0) {
			LOG("[Watchdog] disabled");
			return;
		}

		hangTimeout = spring_secs(hangTimeoutSecs);

		// start the watchdog thread
		hangDetectorThread = new boost::thread(&HangDetectorLoop);

		LOG("[Watchdog] Installed (HangTimeout: %isec)", hangTimeoutSecs);
	}


	void Uninstall()
	{
		if (hangDetectorThread == NULL) {
			return;
		}

		boost::mutex::scoped_lock lock(wdmutex);

		hangDetectorThreadInterrupted = true;
		hangDetectorThread->join();
		delete hangDetectorThread;
		hangDetectorThread = NULL;

		memset(registeredThreadsData, 0, sizeof(registeredThreadsData));
		for (unsigned int i = 0; i < WDT_LAST; ++i)
			registeredThreads[i] = &registeredThreadsData[WDT_LAST];
		memset(threadSlots, 0, sizeof(threadSlots));
		threadNameToNum.clear();
	}
}
示例#10
0
void sleep_spring(int time) { spring_sleep(spring_msecs(time)); }
示例#11
0
namespace Watchdog
{
	struct WatchDogThreadInfo {
		Threading::NativeThreadHandle thread;
		volatile spring_time timer;
		std::string name;
	};
	typedef std::map<Threading::NativeThreadId, WatchDogThreadInfo> ThreadsMap;
	typedef std::map<std::string, Threading::NativeThreadId> ThreadNameToIdMap;
	static ThreadsMap registeredThreads;
	static ThreadNameToIdMap threadNameToId;

	static boost::shared_mutex mutex;
	static boost::thread* hangdetectorthread = NULL;
	static spring_time hangTimeout = spring_msecs(0);


	/** Print stack traces for relevant threads. */
	static void HangHandler(const WatchDogThreadInfo& threadinfo)
	{
		logOutput.Print("Hang detection triggered (in thread: %s) for Spring %s.", threadinfo.name.c_str(), SpringVersion::GetFull().c_str());
	#ifdef USE_GML
		logOutput.Print("MT with %d threads.", gmlThreadCount);
	#endif

		CrashHandler::Stacktrace(threadinfo.thread);
		logOutput.Flush();
	}


	static void HangDetector()
	{
		while (!boost::this_thread::interruption_requested()) {
			{
				boost::shared_lock<boost::shared_mutex> lock(mutex);
				const spring_time curtime = spring_gettime();
				for (ThreadsMap::iterator it=registeredThreads.begin(); it != registeredThreads.end(); ++it) {
					WatchDogThreadInfo& th_info = it->second;
					if (spring_istime(th_info.timer) && (curtime - th_info.timer) > hangTimeout) {
						HangHandler(th_info);
						th_info.timer = curtime;
					}
				}
			}
			boost::this_thread::sleep(boost::posix_time::seconds(1));
		}
	}


	void RegisterThread(std::string name)
	{
		boost::unique_lock<boost::shared_mutex> lock(mutex);
		Threading::NativeThreadHandle thread = Threading::_GetCurrentThread();
		Threading::NativeThreadId threadId = Threading::_GetCurrentThreadId();
		WatchDogThreadInfo& th_info = registeredThreads[threadId];
		th_info.thread = thread;
		th_info.timer = spring_gettime();
		th_info.name = name;
		threadNameToId[name] = threadId;
	}


	/*void DeregisterThread()
	{
		boost::unique_lock<boost::shared_mutex> lock(mutex);
		//FIXME ADD CODE
	}*/


	void ClearTimer(bool disable, Threading::NativeThreadId* _threadId)
	{
		boost::shared_lock<boost::shared_mutex> lock(mutex);
		if (!hangdetectorthread)
			return; //! Watchdog isn't running
		Threading::NativeThreadId threadId;
			if (_threadId) {
			threadId = *_threadId;
		} else {
			threadId = Threading::_GetCurrentThreadId();
		}
		ThreadsMap::iterator it = registeredThreads.find(threadId);
		if (it == registeredThreads.end()) {
			return;
		}
		assert(it != registeredThreads.end()); //! the thread isn't registered!

		WatchDogThreadInfo& th_info = it->second;
		th_info.timer = disable ? spring_notime : spring_gettime();
	}


	void ClearTimer(const std::string& name, bool disable)
	{
		boost::shared_lock<boost::shared_mutex> lock(mutex);
		if (!hangdetectorthread)
			return; //! Watchdog isn't running
		ThreadNameToIdMap::iterator it = threadNameToId.find(name);
		if (it == threadNameToId.end()) {
			logOutput.Print("[Watchdog::ClearTimer] No thread found named \"%s\".", name.c_str());
			return;
		}
		ClearTimer(disable, &(it->second));
	}


	void Install()
	{
		int hangTimeoutSecs = configHandler->Get("HangTimeout", 0);

		//! HangTimeout = -1 to force disable hang detection
		if (hangTimeoutSecs < 0)
			return;
		if (hangTimeoutSecs == 0)
			hangTimeoutSecs = 10;

		hangTimeout = spring_secs(hangTimeoutSecs);

		//! register (this) mainthread
		RegisterThread("main");

		//! start the watchdog thread
		hangdetectorthread = new boost::thread(&HangDetector);

		logOutput.Print("[Watchdog] Installed (timeout: %isec)", hangTimeoutSecs);
	}


	void Uninstall()
	{
		if (hangdetectorthread) {
			hangdetectorthread->interrupt();
			hangdetectorthread->join();
			delete hangdetectorthread;
			hangdetectorthread = NULL;

			boost::unique_lock<boost::shared_mutex> lock(mutex);
			registeredThreads.clear();
			threadNameToId.clear();
		}
	}
}
示例#12
0
namespace CrashHandler {

boost::thread* hangdetectorthread = NULL;
volatile int keepRunning = 1;
// watchdog timers
volatile spring_time simwdt = spring_notime, drawwdt = spring_notime;
spring_time hangTimeout = spring_msecs(0);
volatile bool gameLoading = false;

void SigAbrtHandler(int signal)
{
	// cause an exception if on windows
	// TODO FIXME do a proper stacktrace dump here
	*((int*)(0)) = 0;
}

// Set this to the desired printf style output function.
// Currently we write through the logOutput class to infolog.txt
#define PRINT logOutput.Print

/** Convert exception code to human readable string. */
static const char *ExceptionName(DWORD exceptionCode)
{
	switch (exceptionCode) {
		case EXCEPTION_ACCESS_VIOLATION:         return "Access violation";
		case EXCEPTION_DATATYPE_MISALIGNMENT:    return "Datatype misalignment";
		case EXCEPTION_BREAKPOINT:               return "Breakpoint";
		case EXCEPTION_SINGLE_STEP:              return "Single step";
		case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:    return "Array bounds exceeded";
		case EXCEPTION_FLT_DENORMAL_OPERAND:     return "Float denormal operand";
		case EXCEPTION_FLT_DIVIDE_BY_ZERO:       return "Float divide by zero";
		case EXCEPTION_FLT_INEXACT_RESULT:       return "Float inexact result";
		case EXCEPTION_FLT_INVALID_OPERATION:    return "Float invalid operation";
		case EXCEPTION_FLT_OVERFLOW:             return "Float overflow";
		case EXCEPTION_FLT_STACK_CHECK:          return "Float stack check";
		case EXCEPTION_FLT_UNDERFLOW:            return "Float underflow";
		case EXCEPTION_INT_DIVIDE_BY_ZERO:       return "Integer divide by zero";
		case EXCEPTION_INT_OVERFLOW:             return "Integer overflow";
		case EXCEPTION_PRIV_INSTRUCTION:         return "Privileged instruction";
		case EXCEPTION_IN_PAGE_ERROR:            return "In page error";
		case EXCEPTION_ILLEGAL_INSTRUCTION:      return "Illegal instruction";
		case EXCEPTION_NONCONTINUABLE_EXCEPTION: return "Noncontinuable exception";
		case EXCEPTION_STACK_OVERFLOW:           return "Stack overflow";
		case EXCEPTION_INVALID_DISPOSITION:      return "Invalid disposition";
		case EXCEPTION_GUARD_PAGE:               return "Guard page";
		case EXCEPTION_INVALID_HANDLE:           return "Invalid handle";
	}
	return "Unknown exception";
}

static void initImageHlpDll() {

	char userSearchPath[8];
	STRCPY(userSearchPath, ".");
	// Initialize IMAGEHLP.DLL
	SymInitialize(GetCurrentProcess(), userSearchPath, TRUE);
}

/** Print out a stacktrace. */
static void Stacktrace(LPEXCEPTION_POINTERS e, HANDLE hThread = INVALID_HANDLE_VALUE) {
	PIMAGEHLP_SYMBOL pSym;
	STACKFRAME sf;
	HANDLE process, thread;
	DWORD dwModBase, Disp;
	BOOL more = FALSE;
	int count = 0;
	char modname[MAX_PATH];

	pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, 16384);

	BOOL suspended = FALSE;
	CONTEXT c;
	if (e) {
		c = *e->ContextRecord;
		thread = GetCurrentThread();
	} else {
		SuspendThread(hThread);
		suspended = TRUE;
		memset(&c, 0, sizeof(CONTEXT));
		c.ContextFlags = CONTEXT_FULL;
		// FIXME: This does not work if you want to dump the current thread's stack
		if (!GetThreadContext(hThread, &c)) {
			ResumeThread(hThread);
			return;
		}
		thread = hThread;
	}

	ZeroMemory(&sf, sizeof(sf));
	sf.AddrPC.Offset = c.Eip;
	sf.AddrStack.Offset = c.Esp;
	sf.AddrFrame.Offset = c.Ebp;
	sf.AddrPC.Mode = AddrModeFlat;
	sf.AddrStack.Mode = AddrModeFlat;
	sf.AddrFrame.Mode = AddrModeFlat;

	process = GetCurrentProcess();

	// use globalalloc to reduce risk for allocator related deadlock
	char* printstrings = (char*)GlobalAlloc(GMEM_FIXED, 0);

	bool containsOglDll = false;
	while (true) {
		more = StackWalk(
			IMAGE_FILE_MACHINE_I386, // TODO: fix this for 64 bit windows?
			process,
			thread,
			&sf,
			&c,
			NULL,
			SymFunctionTableAccess,
			SymGetModuleBase,
			NULL
		);
		if (!more || sf.AddrFrame.Offset == 0) {
			break;
		}

		dwModBase = SymGetModuleBase(process, sf.AddrPC.Offset);

		if (dwModBase) {
			GetModuleFileName((HINSTANCE)dwModBase, modname, MAX_PATH);
		} else {
			strcpy(modname, "Unknown");
		}

		pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL);
		pSym->MaxNameLength = MAX_PATH;

		char *printstringsnew = (char *)GlobalAlloc(GMEM_FIXED, (count + 1) * BUFFER_SIZE);
		memcpy(printstringsnew, printstrings, count * BUFFER_SIZE);
		GlobalFree(printstrings);
		printstrings = printstringsnew;

		if (SymGetSymFromAddr(process, sf.AddrPC.Offset, &Disp, pSym)) {
			// This is the code path taken on VC if debugging syms are found.
			SNPRINTF(printstrings + count * BUFFER_SIZE, BUFFER_SIZE, "(%d) %s(%s+%#0lx) [0x%08lX]", count, modname, pSym->Name, Disp, sf.AddrPC.Offset);
		} else {
			// This is the code path taken on MinGW, and VC if no debugging syms are found.
			SNPRINTF(printstrings + count * BUFFER_SIZE, BUFFER_SIZE, "(%d) %s [0x%08lX]", count, modname, sf.AddrPC.Offset);
		}

		// OpenGL lib names (ATI): "atioglxx.dll" "atioglx2.dll"
		containsOglDll = containsOglDll || strstr(modname, "atiogl");
		// OpenGL lib names (Nvidia): "nvoglnt.dll" "nvoglv32.dll" "nvoglv64.dll" (last one is a guess)
		containsOglDll = containsOglDll || strstr(modname, "nvogl");

		++count;
	}

	if (containsOglDll) {
		PRINT("This stack trace indicates a problem with your graphic card driver. "
		      "Please try upgrading or downgrading it. "
		      "Specifically recommended is the latest driver, and one that is as old as your graphic card. "
		      "Make sure to use a driver removal utility, before installing other drivers.");
	}

	if (suspended) {
		ResumeThread(hThread);
	}

	for (int i = 0; i < count; ++i) {
		PRINT("%s", printstrings + i * BUFFER_SIZE);
	}

	GlobalFree(printstrings);

	GlobalFree(pSym);
}

/** Callback for SymEnumerateModules */
#if _MSC_VER >= 1500
static BOOL CALLBACK EnumModules(PCSTR moduleName, ULONG baseOfDll, PVOID userContext)
{
	PRINT("0x%08lx\t%s", baseOfDll, moduleName);
	return TRUE;
}
#else // _MSC_VER >= 1500
static BOOL CALLBACK EnumModules(LPSTR moduleName, DWORD baseOfDll, PVOID userContext)
{
	PRINT("0x%08lx\t%s", baseOfDll, moduleName);
	return TRUE;
}
#endif // _MSC_VER >= 1500

/** Called by windows if an exception happens. */
static LONG CALLBACK ExceptionHandler(LPEXCEPTION_POINTERS e)
{
	// Prologue.
	logOutput.SetSubscribersEnabled(false);
	PRINT("Spring %s has crashed.", SpringVersion::GetFull().c_str());
#ifdef USE_GML
	PRINT("MT with %d threads.", gmlThreadCount);
#endif
	initImageHlpDll();

	// Record exception info.
	PRINT("Exception: %s (0x%08lx)", ExceptionName(e->ExceptionRecord->ExceptionCode), e->ExceptionRecord->ExceptionCode);
	PRINT("Exception Address: 0x%08lx", (unsigned long int) (PVOID) e->ExceptionRecord->ExceptionAddress);

	// Record list of loaded DLLs.
	PRINT("DLL information:");
	SymEnumerateModules(GetCurrentProcess(), EnumModules, NULL);

	// Record stacktrace.
	PRINT("Stacktrace:");
	Stacktrace(e);

	// Unintialize IMAGEHLP.DLL
	SymCleanup(GetCurrentProcess());

	// Cleanup.
	SDL_Quit();
	logOutput.End();  // Stop writing to log.
	// FIXME: update closing of demo to new netcode

	// Inform user.
	char dir[MAX_PATH], msg[MAX_PATH+200];
	GetCurrentDirectory(sizeof(dir) - 1, dir);
	SNPRINTF(msg, sizeof(msg),
		"Spring has crashed.\n\n"
		"A stacktrace has been written to:\n"
		"%s\\infolog.txt", dir);
	MessageBox(NULL, msg, "Spring: Unhandled exception", 0);

	// this seems to silently close the application
	return EXCEPTION_EXECUTE_HANDLER;

	// this triggers the microsoft "application has crashed" error dialog
	//return EXCEPTION_CONTINUE_SEARCH;

	// in practice, 100% CPU usage but no continuation of execution
	// (tested segmentation fault and division by zero)
	//return EXCEPTION_CONTINUE_EXECUTION;
}

/** Print stack traces for relevant threads. */
void HangHandler(bool simhang)
{
	PRINT("Hang detection triggered %sfor Spring %s.", simhang ? "(in sim thread) " : "", SpringVersion::GetFull().c_str());
#ifdef USE_GML
	PRINT("MT with %d threads.", gmlThreadCount);
#endif

	initImageHlpDll();

	// Record list of loaded DLLs.
	PRINT("DLL information:");
	SymEnumerateModules(GetCurrentProcess(), EnumModules, NULL);


	if (drawthread != INVALID_HANDLE_VALUE) {
		// Record stacktrace.
		PRINT("Stacktrace:");
		Stacktrace(NULL, drawthread);
	}

#if defined(USE_GML) && GML_ENABLE_SIM
	if (gmlMultiThreadSim && simthread != INVALID_HANDLE_VALUE) {
		PRINT("Stacktrace (sim):");
		Stacktrace(NULL, simthread);
	}
#endif

	// Unintialize IMAGEHLP.DLL
	SymCleanup(GetCurrentProcess());

	logOutput.Flush();
}

void HangDetector() {
	while (keepRunning) {
		// increase multiplier during game load to prevent false positives e.g. during pathing
		const int hangTimeMultiplier = CrashHandler::gameLoading? 3 : 1;
		const spring_time hangtimeout = spring_msecs(spring_tomsecs(hangTimeout) * hangTimeMultiplier);

		spring_time curwdt = spring_gettime();
#if defined(USE_GML) && GML_ENABLE_SIM
		if (gmlMultiThreadSim) {
			spring_time cursimwdt = simwdt;
			if (spring_istime(cursimwdt) && curwdt > cursimwdt && (curwdt - cursimwdt) > hangtimeout) {
				HangHandler(true);
				simwdt = curwdt;
			}
		}
#endif
		spring_time curdrawwdt = drawwdt;
		if (spring_istime(curdrawwdt) && curwdt > curdrawwdt && (curwdt - curdrawwdt) > hangtimeout) {
			HangHandler(false);
			drawwdt = curwdt;
		}

		spring_sleep(spring_secs(1));
	}
}


void InstallHangHandler() {
	DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(),
					&drawthread, 0, TRUE, DUPLICATE_SAME_ACCESS);
	int hangTimeoutMS = configHandler->Get("HangTimeout", 0);
	CrashHandler::gameLoading = false;
	// HangTimeout = -1 to force disable hang detection
	if (hangTimeoutMS >= 0) {
		if (hangTimeoutMS == 0) {
			hangTimeoutMS = 10;
		}
		hangTimeout = spring_secs(hangTimeoutMS);
		hangdetectorthread = new boost::thread(&HangDetector);
	}
	InitializeSEH();
}

void ClearDrawWDT(bool disable) { drawwdt = disable ? spring_notime : spring_gettime(); }
void ClearSimWDT(bool disable) { simwdt = disable ? spring_notime : spring_gettime(); }

void GameLoading(bool loading) { CrashHandler::gameLoading = loading; }

void UninstallHangHandler()
{
	if (hangdetectorthread) {
		keepRunning = 0;
		hangdetectorthread->join();
		delete hangdetectorthread;
	}
	if (drawthread != INVALID_HANDLE_VALUE) {
		CloseHandle(drawthread);
	}
	if (simthread != INVALID_HANDLE_VALUE) {
		CloseHandle(simthread);
	}
}

/** Install crash handler. */
void Install()
{
	SetUnhandledExceptionFilter(ExceptionHandler);
	signal(SIGABRT, SigAbrtHandler);
}

/** Uninstall crash handler. */
void Remove()
{
	SetUnhandledExceptionFilter(NULL);
	signal(SIGABRT, SIG_DFL);
}

}; // namespace CrashHandler