/* * OpenClonk, http://www.openclonk.org * * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/ * Copyright (c) 2009-2016, The OpenClonk Team and contributors * * Distributed under the terms of the ISC license; see accompanying file * "COPYING" for details. * * "Clonk" is a registered trademark of Matthes Bender, used with permission. * See accompanying file "TRADEMARK" for details. * * To redistribute this file separately, substitute the full license texts * for the above references. */ #include "C4Include.h" #include "platform/StdScheduler.h" #ifdef HAVE_IO_H #include #endif #ifdef HAVE_SHARE_H #include #endif #ifdef _WIN32 #include #endif // *** StdSchedulerProc // Keep calling Execute until timeout has elapsed bool StdSchedulerProc::ExecuteUntil(int iTimeout) { // Infinite? if (iTimeout < 0) for (;;) if (!Execute()) return false; // Calculate endpoint C4TimeMilliseconds tStopTime = C4TimeMilliseconds::Now() + iTimeout; for (;;) { // Call execute with given timeout if (!Execute(std::max(iTimeout, 0))) return false; // Calculate timeout C4TimeMilliseconds tTime = C4TimeMilliseconds::Now(); if (tTime >= tStopTime) break; iTimeout = tStopTime - tTime; } // All ok. return true; } // *** StdScheduler StdScheduler::StdScheduler() { Add(&Unblocker); } StdScheduler::~StdScheduler() { Clear(); } void StdScheduler::Clear() { while (procs.size() > 0) Remove(procs[procs.size()-1]); } void StdScheduler::Set(StdSchedulerProc **ppnProcs, int inProcCnt) { // Remove previous data Clear(); // Copy new for (int i = 0; i < inProcCnt; i++) Add(ppnProcs[i]); } void StdScheduler::Add(StdSchedulerProc *pProc) { // Already added to some scheduler if (pProc->scheduler) return; // Add procs.push_back(pProc); pProc->scheduler = this; Added(pProc); } void StdScheduler::Remove(StdSchedulerProc *pProc) { // :o ? if (pProc->scheduler != this) return; Removing(pProc); pProc->scheduler = nullptr; auto pos = std::find(procs.begin(), procs.end(), pProc); if (pos != procs.end()) procs.erase(pos); } void StdSchedulerProc::Changed() { auto s = scheduler; if (s) s->Changed(this); } C4TimeMilliseconds StdSchedulerProc::GetNextTick(C4TimeMilliseconds tNow) { return C4TimeMilliseconds::PositiveInfinity; } C4TimeMilliseconds StdScheduler::GetNextTick(C4TimeMilliseconds tNow) { C4TimeMilliseconds tProcTick = C4TimeMilliseconds::PositiveInfinity; for (auto proc : procs) { tProcTick = std::min(tProcTick, proc->GetNextTick(tNow)); } return tProcTick; } bool StdScheduler::ScheduleProcs(int iTimeout) { // Needs at least one process to work properly if (!procs.size()) return false; // Get timeout C4TimeMilliseconds tNow = C4TimeMilliseconds::Now(); C4TimeMilliseconds tProcTick = GetNextTick(tNow); if (iTimeout == -1 || tNow + iTimeout > tProcTick) { iTimeout = std::max(tProcTick - tNow, 0); } bool old = isInManualLoop; isInManualLoop = true; bool res = DoScheduleProcs(iTimeout); isInManualLoop = old; return res; } void StdScheduler::UnBlock() { Unblocker.Notify(); } // *** StdSchedulerThread StdSchedulerThread::StdSchedulerThread() = default; StdSchedulerThread::~StdSchedulerThread() { Clear(); } void StdSchedulerThread::Clear() { // Stop thread if (fThread) Stop(); // Clear scheduler StdScheduler::Clear(); } void StdSchedulerThread::Set(StdSchedulerProc **ppProcs, int iProcCnt) { // Thread is running? Stop it first bool fGotThread = fThread; if (fGotThread) Stop(); // Set StdScheduler::Set(ppProcs, iProcCnt); // Restart if (fGotThread) Start(); } void StdSchedulerThread::Add(StdSchedulerProc *pProc) { // Thread is running? Stop it first bool fGotThread = fThread; if (fGotThread) Stop(); // Set StdScheduler::Add(pProc); // Restart if (fGotThread) Start(); } void StdSchedulerThread::Remove(StdSchedulerProc *pProc) { // Thread is running? Stop it first bool fGotThread = fThread; if (fGotThread) Stop(); // Set StdScheduler::Remove(pProc); // Restart if (fGotThread) Start(); } bool StdSchedulerThread::Start() { // already running? stop if (fThread) Stop(); // begin thread fRunThreadRun = true; #ifdef HAVE_WINTHREAD iThread = _beginthread(_ThreadFunc, 0, this); fThread = (iThread != -1); #elif defined(HAVE_PTHREAD) fThread = !pthread_create(&Thread, nullptr, _ThreadFunc, this); #endif // success? return fThread; } void StdSchedulerThread::Stop() { // Not running? if (!fThread) return; // Set flag fRunThreadRun = false; // Unblock UnBlock(); #ifdef HAVE_WINTHREAD // Wait for thread to terminate itself HANDLE hThread = reinterpret_cast(iThread); if (WaitForSingleObject(hThread, 10000) == WAIT_TIMEOUT) // ... or kill it in case it refuses to do so TerminateThread(hThread, -1); #elif defined(HAVE_PTHREAD) // wait for thread to terminate itself // (without security - let's trust these unwashed hackers for once) pthread_join(Thread, nullptr); #endif fThread = false; // ok return; } #ifdef HAVE_WINTHREAD void __cdecl StdSchedulerThread::_ThreadFunc(void *pPar) { StdSchedulerThread *pThread = reinterpret_cast(pPar); _endthreadex(pThread->ThreadFunc()); } void __cdecl StdThread::_ThreadFunc(void *pPar) { StdThread *pThread = reinterpret_cast(pPar); _endthreadex(pThread->ThreadFunc()); } #elif defined(HAVE_PTHREAD) void *StdSchedulerThread::_ThreadFunc(void *pPar) { StdSchedulerThread *pThread = reinterpret_cast(pPar); return reinterpret_cast(pThread->ThreadFunc()); } void *StdThread::_ThreadFunc(void *pPar) { StdThread *pThread = reinterpret_cast(pPar); return reinterpret_cast(pThread->ThreadFunc()); } #endif unsigned int StdSchedulerThread::ThreadFunc() { StartOnCurrentThread(); // Keep calling Execute until someone gets fed up and calls StopThread() while (fRunThreadRun) ScheduleProcs(1000); return(0); } StdThread::StdThread() = default; bool StdThread::Start() { // already running? stop if (fStarted) Stop(); // begin thread fStopSignaled = false; #ifdef HAVE_WINTHREAD iThread = _beginthread(_ThreadFunc, 0, this); fStarted = (iThread != -1); #elif defined(HAVE_PTHREAD) fStarted = !pthread_create(&Thread, nullptr, _ThreadFunc, this); #endif // success? return fStarted; } void StdThread::SignalStop() { // Not running? if (!fStarted) return; // Set flag fStopSignaled = true; } void StdThread::Stop() { // Not running? if (!fStarted) return; // Set flag fStopSignaled = true; #ifdef HAVE_WINTHREAD // Wait for thread to terminate itself HANDLE hThread = reinterpret_cast(iThread); if (WaitForSingleObject(hThread, 10000) == WAIT_TIMEOUT) // ... or kill it in case it refuses to do so TerminateThread(hThread, -1); #elif defined(HAVE_PTHREAD) // wait for thread to terminate itself // (whithout security - let's trust these unwashed hackers for once) pthread_join(Thread, nullptr); #endif fStarted = false; // ok return; } unsigned int StdThread::ThreadFunc() { // Keep calling Execute until someone gets fed up and calls Stop() while (!IsStopSignaled()) Execute(); // Handle deletion if (IsSelfDestruct()) { fStarted = false; // reset start flag to avoid Stop() call delete this; } return(0); } bool StdThread::IsStopSignaled() { return fStopSignaled; }