openclonk/src/platform/StdScheduler.cpp

346 lines
7.3 KiB
C++

/*
* 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 <io.h>
#endif
#ifdef HAVE_SHARE_H
#include <share.h>
#endif
#ifdef _WIN32
#include <process.h>
#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<decltype(iTimeout)>(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<HANDLE>(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<StdSchedulerThread *>(pPar);
_endthreadex(pThread->ThreadFunc());
}
void __cdecl StdThread::_ThreadFunc(void *pPar)
{
StdThread *pThread = reinterpret_cast<StdThread *>(pPar);
_endthreadex(pThread->ThreadFunc());
}
#elif defined(HAVE_PTHREAD)
void *StdSchedulerThread::_ThreadFunc(void *pPar)
{
StdSchedulerThread *pThread = reinterpret_cast<StdSchedulerThread *>(pPar);
return reinterpret_cast<void *>(pThread->ThreadFunc());
}
void *StdThread::_ThreadFunc(void *pPar)
{
StdThread *pThread = reinterpret_cast<StdThread *>(pPar);
return reinterpret_cast<void *>(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<HANDLE>(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;
}