OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
StdScheduler.h
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 /* A simple scheduler for ccoperative multitasking */
17 
18 #ifndef STDSCHEDULER_H
19 #define STDSCHEDULER_H
20 
21 #include "platform/StdSync.h"
22 
23 // Events are Windows-specific
24 #ifdef _WIN32
25 #define STDSCHEDULER_USE_EVENTS
26 #define HAVE_WINTHREAD
27 #define STDSCHEDULER_EVENT_MESSAGE INVALID_HANDLE_VALUE
28 struct pollfd;
29 #ifndef STDSCHEDULER_USE_EVENTS
31 #include <winsock2.h>
32 #endif // STDSCHEDULER_USE_EVENTS
33 #else // _WIN32
34 #ifdef HAVE_POLL_H
35 #include <poll.h>
36 #else // HAVE_POLL_H
37 #include <sys/select.h>
38 #endif // HAVE_POLL_H
39 #ifdef HAVE_PTHREAD
40 #include <pthread.h>
41 #endif // HAVE_PTHREAD
42 #ifdef __APPLE__
43 #include <sched.h>
44 #endif
45 #endif // _WIN32
46 
47 typedef struct _GMainLoop GMainLoop;
48 
49 // Abstract class for a process
51 {
52 private:
53  class StdScheduler *scheduler{nullptr};
54 protected:
55  void Changed();
56 public:
57 
58  StdSchedulerProc() = default;
59  virtual ~StdSchedulerProc() = default;
60 
61  // Do whatever the process wishes to do. Should not block longer than the timeout value.
62  // Is called whenever the process is signaled or a timeout occurs.
63  virtual bool Execute(int iTimeout = -1, pollfd * readyfds = nullptr) = 0;
64 
65  // As Execute, but won't return until the given timeout value has elapsed or a failure occurs
66  bool ExecuteUntil(int iTimeout = -1);
67 
68  // Signal for calling Execute()
69 #ifdef STDSCHEDULER_USE_EVENTS
70  virtual HANDLE GetEvent() { return nullptr; }
71 #else
72  virtual void GetFDs(std::vector<struct pollfd> &) { }
73 #endif
74 
75  // Call Execute() after this time has elapsed
77 
78  // Is the process signal currently set?
79  bool IsSignaled();
80 
81  // Is this the expensive game tick?
82  virtual bool IsLowPriority() { return false; }
83  virtual bool IsNotify() { return false; }
84  virtual uint32_t TimerInterval() { return 0; }
85 
86  friend class StdScheduler;
87 
88 };
89 
90 // A simple timer proc
92 {
93 public:
94  CStdTimerProc(uint32_t iDelay) : tLastTimer(C4TimeMilliseconds::NegativeInfinity), iDelay(iDelay) { }
95  ~CStdTimerProc() override { Set(); }
96 
97 private:
98  C4TimeMilliseconds tLastTimer;
99  uint32_t iDelay;
100 
101 public:
102  void Set()
103  {
105  }
106  void SetDelay(uint32_t inDelay) { iDelay = inDelay; Changed(); }
108  {
110  if (tTime < tLastTimer + iDelay) return false;
111  // Compensate light drifting
112  int32_t iDrift = tTime - (tLastTimer + iDelay); // a positive time difference because of above check
113  tLastTimer = tTime - std::min(iDrift, (int32_t) iDelay / 2);
114  return true;
115  }
116 
117  // StdSchedulerProc override
119  {
120  return tLastTimer + iDelay;
121  }
122  uint32_t TimerInterval() override { return iDelay; }
123 };
124 
126 {
127 public:
129  virtual void OnSec1Timer() = 0;
130 protected:
131  bool Execute(int, pollfd *) override
132  {
133  if (CheckAndReset())
134  OnSec1Timer();
135  return true;
136  }
137 };
138 
139 // A simple alertable proc
141 {
142 public:
143  CStdNotifyProc();
144 
145  void Notify();
146  bool CheckAndReset();
147  bool IsNotify() override { return true; }
148 
149 #ifdef STDSCHEDULER_USE_EVENTS
150  ~CStdNotifyProc() override = default;
151 
152  // StdSchedulerProc override
153  HANDLE GetEvent() override { return Event.GetEvent(); }
154 
155 private:
156  CStdEvent Event;
157 
158 #else // STDSCHEDULER_USE_EVENTS
159  ~CStdNotifyProc() override;
160 
161  // StdSchedulerProc override
162  void GetFDs(std::vector<struct pollfd> & checkfds) override;
163 
164 private:
165  int fds[2];
166 
167 #endif
168 };
169 
170 #ifdef STDSCHEDULER_USE_EVENTS
172 {
173 public:
174  CStdMultimediaTimerProc(uint32_t iDelay);
175  ~CStdMultimediaTimerProc() override;
176 
177 private:
178  static int iTimePeriod;
179  uint32_t uCriticalTimerDelay;
180 
181  UINT idCriticalTimer,uCriticalTimerResolution;
182  CStdEvent Event;
183  bool Check() { return Event.WaitFor(0); }
184 
185 public:
186 
187  void SetDelay(uint32_t iDelay);
188  void Set() { Event.Set(); }
189  bool CheckAndReset();
190 
191  // StdSchedulerProc overrides
192  HANDLE GetEvent() override { return Event.GetEvent(); }
193 
194 };
195 
196 #elif defined(HAVE_SYS_TIMERFD_H)
197 // timer proc using a timerfd
199 {
200 public:
201  CStdMultimediaTimerProc(uint32_t iDelay);
202  ~CStdMultimediaTimerProc() override;
203 
204 private:
205  int fd;
206 
207 public:
208  void Set();
209  void SetDelay(uint32_t inDelay);
210  bool CheckAndReset();
211  // StdSchedulerProc overrides
212  void GetFDs(std::vector<struct pollfd> & checkfds) override;
213 };
214 
215 #else
216 #define CStdMultimediaTimerProc CStdTimerProc
217 #endif
218 
219 // A simple process scheduler
221 {
222 public:
223  StdScheduler();
224  virtual ~StdScheduler();
225 
226 private:
227  // Process list
228  std::vector<StdSchedulerProc*> procs;
229  bool isInManualLoop{false};
230 
231  // Unblocker
232  class NoopNotifyProc : public CStdNotifyProc
233  {
234  public: bool Execute(int, pollfd *) override { CheckAndReset(); return true; }
235  };
236  NoopNotifyProc Unblocker;
237 
238  // Dummy lists (preserved to reduce allocs)
239 #ifdef STDSCHEDULER_USE_EVENTS
240  std::vector<HANDLE> eventHandles;
241  std::vector<StdSchedulerProc*> eventProcs;
242 #endif
243 
244 public:
245  int getProcCnt() const { return procs.size()-1; } // ignore internal NoopNotifyProc
246  bool hasProc(StdSchedulerProc *pProc) { return std::find(procs.begin(), procs.end(), pProc) != procs.end(); }
247  bool IsInManualLoop() { return isInManualLoop; }
248 
249  void Clear();
250  void Set(StdSchedulerProc **ppProcs, int iProcCnt);
251  void Add(StdSchedulerProc *pProc);
252  void Remove(StdSchedulerProc *pProc);
253 
254  // extra events for above Add/Remove methods
255  void Added(StdSchedulerProc *pProc);
256  void Removing(StdSchedulerProc *pProc);
257  // called by StdSchedulerProcs when something important about their configuration changed
258  void Changed(StdSchedulerProc *pProc);
259  // needs to be called on thread tasks for this scheduler are meant to be run on
260  void StartOnCurrentThread();
261 
263  bool ScheduleProcs(int iTimeout = 1000/36);
264  void UnBlock();
265 
266 protected:
267  // overridable
268  virtual void OnError(StdSchedulerProc *) { }
269  virtual bool DoScheduleProcs(int iTimeout);
270 };
271 
272 // A simple process scheduler thread
274 {
275 public:
277  ~StdSchedulerThread() override;
278 
279 private:
280 
281  // thread control
282  bool fRunThreadRun;
283 
284  bool fThread{false};
285 #ifdef HAVE_WINTHREAD
286  uintptr_t iThread;
287 #elif defined(HAVE_PTHREAD)
288  pthread_t Thread;
289 #endif
290 
291 public:
292  void Clear();
293  void Set(StdSchedulerProc **ppProcs, int iProcCnt);
294  void Add(StdSchedulerProc *pProc);
295  void Remove(StdSchedulerProc *pProc);
296 
297  bool Start();
298  void Stop();
299 
300 private:
301 
302  // thread func
303 #ifdef HAVE_WINTHREAD
304  static void __cdecl _ThreadFunc(void *);
305 #elif defined(HAVE_PTHREAD)
306  static void *_ThreadFunc(void *);
307 #endif
308  unsigned int ThreadFunc();
309 
310 };
311 
313 {
314 private:
315  bool fStarted{false};
316  bool fStopSignaled{false};
317 
318 #ifdef HAVE_WINTHREAD
319  uintptr_t iThread;
320 #elif defined(HAVE_PTHREAD)
321  pthread_t Thread;
322 #endif
323 
324 public:
325  StdThread();
326  virtual ~StdThread() { Stop(); }
327 
328  bool Start();
329  void SignalStop(); // mark thread to stop but don't wait
330  void Stop();
331 
332  bool IsStarted() { return fStarted; }
333 
334 protected:
335  virtual void Execute() = 0;
336 
337  bool IsStopSignaled();
338  virtual bool IsSelfDestruct() { return false; } // whether thread should delete itself after execution finished
339 
340 private:
341  // thread func
342 #ifdef HAVE_WINTHREAD
343  static void __cdecl _ThreadFunc(void *);
344 #elif defined(HAVE_PTHREAD)
345  static void *_ThreadFunc(void *);
346 #endif
347  unsigned int ThreadFunc();
348 };
349 
350 #endif
bool hasProc(StdSchedulerProc *pProc)
Definition: StdScheduler.h:246
int getProcCnt() const
Definition: StdScheduler.h:245
bool Execute(int, pollfd *) override
Definition: StdScheduler.h:131
virtual bool Execute(int iTimeout=-1, pollfd *readyfds=nullptr)=0
~CStdTimerProc() override
Definition: StdScheduler.h:95
bool IsStopSignaled()
virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow)
void Removing(StdSchedulerProc *pProc)
void Set(StdSchedulerProc **ppProcs, int iProcCnt)
C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow)
virtual ~StdThread()
Definition: StdScheduler.h:326
virtual void OnError(StdSchedulerProc *)
Definition: StdScheduler.h:268
void GetFDs(std::vector< struct pollfd > &checkfds) override
void Remove(StdSchedulerProc *pProc)
C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow) override
Definition: StdScheduler.h:118
void Add(StdSchedulerProc *pProc)
bool Start()
virtual bool IsSelfDestruct()
Definition: StdScheduler.h:338
bool CheckAndReset()
Definition: StdScheduler.h:107
StdSchedulerProc()=default
CStdTimerProc(uint32_t iDelay)
Definition: StdScheduler.h:94
uint32_t TimerInterval() override
Definition: StdScheduler.h:122
bool WaitFor(int)
Definition: StdSync.h:161
virtual uint32_t TimerInterval()
Definition: StdScheduler.h:84
void SetDelay(uint32_t inDelay)
Definition: StdScheduler.h:106
#define CStdMultimediaTimerProc
Definition: StdScheduler.h:216
bool ExecuteUntil(int iTimeout=-1)
void SignalStop()
bool CheckAndReset()
~StdSchedulerThread() override
virtual ~StdSchedulerProc()=default
~CStdNotifyProc() override
virtual bool IsLowPriority()
Definition: StdScheduler.h:82
virtual void Execute()=0
void Remove(StdSchedulerProc *pProc)
virtual void GetFDs(std::vector< struct pollfd > &)
Definition: StdScheduler.h:72
void StartOnCurrentThread()
virtual ~StdScheduler()
virtual void OnSec1Timer()=0
void Set()
Definition: StdSync.h:158
void Changed(StdSchedulerProc *pProc)
bool IsNotify() override
Definition: StdScheduler.h:147
bool ScheduleProcs(int iTimeout=1000/36)
virtual bool DoScheduleProcs(int iTimeout)
void Set(StdSchedulerProc **ppProcs, int iProcCnt)
void Added(StdSchedulerProc *pProc)
bool IsInManualLoop()
Definition: StdScheduler.h:247
struct _GMainLoop GMainLoop
Definition: StdScheduler.h:47
virtual bool IsNotify()
Definition: StdScheduler.h:83
static C4TimeMilliseconds Now()
void Add(StdSchedulerProc *pProc)
bool IsStarted()
Definition: StdScheduler.h:332