forked from Mirrors/openclonk
278 lines
5.9 KiB
C++
278 lines
5.9 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 2006, 2009 Peter Wortmann
|
|
* Copyright (c) 2006, 2009 Günther Brammer
|
|
* Copyright (c) 2008 Sven Eberhardt
|
|
* Copyright (c) 2009 Nicolas Hake
|
|
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
|
|
*
|
|
* Portions might be copyrighted by other authors who have contributed
|
|
* to OpenClonk.
|
|
*
|
|
* Permission to use, copy, modify, and/or distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
* See isc_license.txt for full license and disclaimer.
|
|
*
|
|
* "Clonk" is a registered trademark of Matthes Bender.
|
|
* See clonk_trademark_license.txt for full license.
|
|
*/
|
|
/* A simple scheduler for ccoperative multitasking */
|
|
|
|
#ifndef STDSCHEDULER_H
|
|
#define STDSCHEDULER_H
|
|
|
|
#include "StdSync.h"
|
|
|
|
// Events are Windows-specific
|
|
#ifdef _WIN32
|
|
#define STDSCHEDULER_USE_EVENTS
|
|
#define HAVE_WINTHREAD
|
|
#define STDSCHEDULER_EVENT_MESSAGE INVALID_HANDLE_VALUE
|
|
struct pollfd;
|
|
#ifndef STDSCHEDULER_USE_EVENTS
|
|
#include <C4windowswrapper.h>
|
|
#include <winsock2.h>
|
|
#endif // STDSCHEDULER_USE_EVENTS
|
|
#else // _WIN32
|
|
#ifdef HAVE_POLL_H
|
|
#include <poll.h>
|
|
#include <vector>
|
|
#else // HAVE_POLL_H
|
|
#include <sys/select.h>
|
|
#endif // HAVE_POLL_H
|
|
#ifdef HAVE_PTHREAD
|
|
#include <pthread.h>
|
|
#endif // HAVE_PTHREAD
|
|
#endif // _WIN32
|
|
|
|
// helper
|
|
inline int MaxTimeout(int iTimeout1, int iTimeout2)
|
|
{
|
|
return (iTimeout1 == -1 || iTimeout2 == -1) ? -1 : Max(iTimeout1, iTimeout2);
|
|
}
|
|
|
|
typedef struct _GMainLoop GMainLoop;
|
|
|
|
// Abstract class for a process
|
|
class StdSchedulerProc
|
|
{
|
|
public:
|
|
virtual ~StdSchedulerProc() { }
|
|
|
|
// Do whatever the process wishes to do. Should not block longer than the timeout value.
|
|
// Is called whenever the process is signaled or a timeout occurs.
|
|
virtual bool Execute(int iTimeout = -1, pollfd * readyfds = 0) = 0;
|
|
|
|
// As Execute, but won't return until the given timeout value has elapsed or a failure occurs
|
|
bool ExecuteUntil(int iTimeout = -1);
|
|
|
|
// Signal for calling Execute()
|
|
#ifdef STDSCHEDULER_USE_EVENTS
|
|
virtual HANDLE GetEvent() { return 0; }
|
|
#else
|
|
virtual void GetFDs(std::vector<struct pollfd> & FDs) { }
|
|
#endif
|
|
|
|
// Call Execute() after this time has elapsed
|
|
// -1 means no timeout (infinity).
|
|
virtual int GetNextTick(int Now) { return -1; }
|
|
|
|
// Is the process signal currently set?
|
|
bool IsSignaled();
|
|
|
|
};
|
|
|
|
// A simple timer proc
|
|
class CStdTimerProc : public StdSchedulerProc
|
|
{
|
|
public:
|
|
CStdTimerProc(uint32_t iDelay) : iLastTimer(0), iDelay(iDelay) { }
|
|
~CStdTimerProc() { }
|
|
|
|
private:
|
|
uint32_t iLastTimer, iDelay;
|
|
|
|
public:
|
|
void Set() { iLastTimer = 0; }
|
|
void SetDelay(uint32_t inDelay) { iDelay = inDelay; }
|
|
bool Check() { return timeGetTime() >= iLastTimer + iDelay; }
|
|
bool CheckAndReset()
|
|
{
|
|
if (!Check()) return false;
|
|
// Compensate light drifting
|
|
uint32_t iTime = timeGetTime();
|
|
uint32_t iDrift = iTime - iLastTimer - iDelay; // >= 0 because of Check()
|
|
iLastTimer = iTime - Min(iDrift, iDelay / 2);
|
|
return true;
|
|
}
|
|
|
|
// StdSchedulerProc override
|
|
virtual int GetNextTick(int Now)
|
|
{
|
|
return iLastTimer + iDelay;
|
|
}
|
|
};
|
|
|
|
// A simple alertable proc
|
|
class CStdNotifyProc : public StdSchedulerProc
|
|
{
|
|
public:
|
|
CStdNotifyProc();
|
|
~CStdNotifyProc() { }
|
|
|
|
public:
|
|
void Notify();
|
|
bool Check();
|
|
bool CheckAndReset();
|
|
|
|
#ifdef STDSCHEDULER_USE_EVENTS
|
|
|
|
// StdSchedulerProc override
|
|
virtual HANDLE GetEvent() { return Event.GetEvent(); }
|
|
|
|
private:
|
|
CStdEvent Event;
|
|
|
|
#else // STDSCHEDULER_USE_EVENTS
|
|
|
|
private:
|
|
int fds[2];
|
|
public:
|
|
// StdSchedulerProc override
|
|
virtual void GetFDs(std::vector<struct pollfd> & checkfds)
|
|
{
|
|
pollfd pfd = { fds[0], POLLIN, 0 };
|
|
checkfds.push_back(pfd);
|
|
}
|
|
|
|
#endif
|
|
};
|
|
|
|
// A simple process scheduler
|
|
class StdScheduler
|
|
{
|
|
public:
|
|
StdScheduler();
|
|
virtual ~StdScheduler();
|
|
|
|
private:
|
|
// Process list
|
|
StdSchedulerProc **ppProcs;
|
|
int iProcCnt, iProcCapacity;
|
|
|
|
// Unblocker
|
|
class NoopNotifyProc : public CStdNotifyProc
|
|
{
|
|
public: virtual bool Execute(int, pollfd * readyfds) { CheckAndReset(); return true; }
|
|
};
|
|
NoopNotifyProc Unblocker;
|
|
|
|
// Dummy lists (preserved to reduce allocs)
|
|
#ifdef STDSCHEDULER_USE_EVENTS
|
|
HANDLE *pEventHandles;
|
|
StdSchedulerProc **ppEventProcs;
|
|
#endif
|
|
|
|
public:
|
|
int getProcCnt() const { return iProcCnt-1; } // ignore internal NoopNotifyProc
|
|
int getProc(StdSchedulerProc *pProc);
|
|
bool hasProc(StdSchedulerProc *pProc) { return getProc(pProc) >= 0; }
|
|
|
|
void Clear();
|
|
void Set(StdSchedulerProc **ppProcs, int iProcCnt);
|
|
void Add(StdSchedulerProc *pProc);
|
|
void Remove(StdSchedulerProc *pProc);
|
|
|
|
bool ScheduleProcs(int iTimeout = -1);
|
|
void UnBlock();
|
|
|
|
protected:
|
|
// overridable
|
|
virtual void OnError(StdSchedulerProc *pProc) { }
|
|
|
|
private:
|
|
void Enlarge(int iBy);
|
|
|
|
};
|
|
|
|
// A simple process scheduler thread
|
|
class StdSchedulerThread : public StdScheduler
|
|
{
|
|
public:
|
|
StdSchedulerThread();
|
|
virtual ~StdSchedulerThread();
|
|
|
|
private:
|
|
|
|
// thread control
|
|
bool fRunThreadRun, fWait;
|
|
|
|
bool fThread;
|
|
#ifdef HAVE_WINTHREAD
|
|
unsigned long iThread;
|
|
#elif defined(HAVE_PTHREAD)
|
|
pthread_t Thread;
|
|
#endif
|
|
|
|
public:
|
|
void Clear();
|
|
void Set(StdSchedulerProc **ppProcs, int iProcCnt);
|
|
void Add(StdSchedulerProc *pProc);
|
|
void Remove(StdSchedulerProc *pProc);
|
|
|
|
bool Start();
|
|
void Stop();
|
|
|
|
private:
|
|
|
|
// thread func
|
|
#ifdef HAVE_WINTHREAD
|
|
static void __cdecl _ThreadFunc(void *);
|
|
#elif defined(HAVE_PTHREAD)
|
|
static void *_ThreadFunc(void *);
|
|
#endif
|
|
unsigned int ThreadFunc();
|
|
|
|
};
|
|
|
|
class StdThread
|
|
{
|
|
private:
|
|
bool fStarted;
|
|
bool fStopSignaled;
|
|
|
|
#ifdef HAVE_WINTHREAD
|
|
unsigned long iThread;
|
|
#elif defined(HAVE_PTHREAD)
|
|
pthread_t Thread;
|
|
#endif
|
|
|
|
public:
|
|
StdThread();
|
|
virtual ~StdThread() { Stop(); }
|
|
|
|
bool Start();
|
|
void SignalStop(); // mark thread to stop but don't wait
|
|
void Stop();
|
|
|
|
bool IsStarted() { return fStarted; }
|
|
|
|
protected:
|
|
virtual void Execute() = 0;
|
|
|
|
bool IsStopSignaled();
|
|
|
|
private:
|
|
// thread func
|
|
#ifdef HAVE_WINTHREAD
|
|
static void __cdecl _ThreadFunc(void *);
|
|
#elif defined(HAVE_PTHREAD)
|
|
static void *_ThreadFunc(void *);
|
|
#endif
|
|
unsigned int ThreadFunc();
|
|
};
|
|
|
|
#endif
|