forked from Mirrors/openclonk
346 lines
7.3 KiB
C++
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;
|
|
}
|