OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
StdScheduler.cpp
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 #include "C4Include.h"
17 #include "platform/StdScheduler.h"
18 
19 #ifdef HAVE_IO_H
20 #include <io.h>
21 #endif
22 #ifdef HAVE_SHARE_H
23 #include <share.h>
24 #endif
25 
26 #ifdef _WIN32
27 #include <process.h>
28 #endif
29 
30 // *** StdSchedulerProc
31 
32 // Keep calling Execute until timeout has elapsed
34 {
35  // Infinite?
36  if (iTimeout < 0)
37  for (;;)
38  if (!Execute())
39  return false;
40  // Calculate endpoint
41  C4TimeMilliseconds tStopTime = C4TimeMilliseconds::Now() + iTimeout;
42  for (;;)
43  {
44  // Call execute with given timeout
45  if (!Execute(std::max(iTimeout, 0)))
46  return false;
47  // Calculate timeout
49  if (tTime >= tStopTime)
50  break;
51  iTimeout = tStopTime - tTime;
52  }
53  // All ok.
54  return true;
55 }
56 
57 // *** StdScheduler
58 
60 {
61  Add(&Unblocker);
62 }
63 
65 {
66  Clear();
67 }
68 
70 {
71  while (procs.size() > 0)
72  Remove(procs[procs.size()-1]);
73 }
74 
75 void StdScheduler::Set(StdSchedulerProc **ppnProcs, int inProcCnt)
76 {
77  // Remove previous data
78  Clear();
79  // Copy new
80  for (int i = 0; i < inProcCnt; i++)
81  Add(ppnProcs[i]);
82 }
83 
85 {
86  // Already added to some scheduler
87  if (pProc->scheduler)
88  return;
89  // Add
90  procs.push_back(pProc);
91  pProc->scheduler = this;
92 
93  Added(pProc);
94 }
95 
97 {
98  // :o ?
99  if (pProc->scheduler != this)
100  return;
101  Removing(pProc);
102  pProc->scheduler = nullptr;
103  auto pos = std::find(procs.begin(), procs.end(), pProc);
104  if (pos != procs.end())
105  procs.erase(pos);
106 }
107 
109 {
110  auto s = scheduler;
111  if (s)
112  s->Changed(this);
113 }
114 
116 {
118 }
119 
121 {
123  for (auto proc : procs)
124  {
125  tProcTick = std::min(tProcTick, proc->GetNextTick(tNow));
126  }
127  return tProcTick;
128 }
129 
130 bool StdScheduler::ScheduleProcs(int iTimeout)
131 {
132  // Needs at least one process to work properly
133  if (!procs.size()) return false;
134 
135  // Get timeout
137  C4TimeMilliseconds tProcTick = GetNextTick(tNow);
138  if (iTimeout == -1 || tNow + iTimeout > tProcTick)
139  {
140  iTimeout = std::max<decltype(iTimeout)>(tProcTick - tNow, 0);
141  }
142 
143  bool old = isInManualLoop;
144  isInManualLoop = true;
145  bool res = DoScheduleProcs(iTimeout);
146  isInManualLoop = old;
147  return res;
148 }
149 
151 {
152  Unblocker.Notify();
153 }
154 
155 // *** StdSchedulerThread
156 
158 
160 {
161  Clear();
162 }
163 
165 {
166  // Stop thread
167  if (fThread) Stop();
168  // Clear scheduler
170 }
171 
172 void StdSchedulerThread::Set(StdSchedulerProc **ppProcs, int iProcCnt)
173 {
174  // Thread is running? Stop it first
175  bool fGotThread = fThread;
176  if (fGotThread) Stop();
177  // Set
178  StdScheduler::Set(ppProcs, iProcCnt);
179  // Restart
180  if (fGotThread) Start();
181 }
182 
184 {
185  // Thread is running? Stop it first
186  bool fGotThread = fThread;
187  if (fGotThread) Stop();
188  // Set
189  StdScheduler::Add(pProc);
190  // Restart
191  if (fGotThread) Start();
192 }
193 
195 {
196  // Thread is running? Stop it first
197  bool fGotThread = fThread;
198  if (fGotThread) Stop();
199  // Set
200  StdScheduler::Remove(pProc);
201  // Restart
202  if (fGotThread) Start();
203 }
204 
206 {
207  // already running? stop
208  if (fThread) Stop();
209  // begin thread
210  fRunThreadRun = true;
211 #ifdef HAVE_WINTHREAD
212  iThread = _beginthread(_ThreadFunc, 0, this);
213  fThread = (iThread != -1);
214 #elif defined(HAVE_PTHREAD)
215  fThread = !pthread_create(&Thread, nullptr, _ThreadFunc, this);
216 #endif
217  // success?
218  return fThread;
219 }
220 
222 {
223  // Not running?
224  if (!fThread) return;
225  // Set flag
226  fRunThreadRun = false;
227  // Unblock
228  UnBlock();
229 #ifdef HAVE_WINTHREAD
230  // Wait for thread to terminate itself
231  HANDLE hThread = reinterpret_cast<HANDLE>(iThread);
232  if (WaitForSingleObject(hThread, 10000) == WAIT_TIMEOUT)
233  // ... or kill it in case it refuses to do so
234  TerminateThread(hThread, -1);
235 #elif defined(HAVE_PTHREAD)
236  // wait for thread to terminate itself
237  // (without security - let's trust these unwashed hackers for once)
238  pthread_join(Thread, nullptr);
239 #endif
240  fThread = false;
241  // ok
242  return;
243 }
244 
245 #ifdef HAVE_WINTHREAD
246 void __cdecl StdSchedulerThread::_ThreadFunc(void *pPar)
247 {
248  StdSchedulerThread *pThread = reinterpret_cast<StdSchedulerThread *>(pPar);
249  _endthreadex(pThread->ThreadFunc());
250 }
251 void __cdecl StdThread::_ThreadFunc(void *pPar)
252 {
253  StdThread *pThread = reinterpret_cast<StdThread *>(pPar);
254  _endthreadex(pThread->ThreadFunc());
255 }
256 #elif defined(HAVE_PTHREAD)
257 void *StdSchedulerThread::_ThreadFunc(void *pPar)
258 {
259  StdSchedulerThread *pThread = reinterpret_cast<StdSchedulerThread *>(pPar);
260  return reinterpret_cast<void *>(pThread->ThreadFunc());
261 }
262 void *StdThread::_ThreadFunc(void *pPar)
263 {
264  StdThread *pThread = reinterpret_cast<StdThread *>(pPar);
265  return reinterpret_cast<void *>(pThread->ThreadFunc());
266 }
267 #endif
268 
269 unsigned int StdSchedulerThread::ThreadFunc()
270 {
272  // Keep calling Execute until someone gets fed up and calls StopThread()
273  while (fRunThreadRun)
274  ScheduleProcs(1000);
275  return(0);
276 }
277 
278 
279 
280 StdThread::StdThread() = default;
281 
283 {
284  // already running? stop
285  if (fStarted) Stop();
286  // begin thread
287  fStopSignaled = false;
288 #ifdef HAVE_WINTHREAD
289  iThread = _beginthread(_ThreadFunc, 0, this);
290  fStarted = (iThread != -1);
291 #elif defined(HAVE_PTHREAD)
292  fStarted = !pthread_create(&Thread, nullptr, _ThreadFunc, this);
293 #endif
294  // success?
295  return fStarted;
296 }
297 
299 {
300  // Not running?
301  if (!fStarted) return;
302  // Set flag
303  fStopSignaled = true;
304 }
305 
307 {
308  // Not running?
309  if (!fStarted) return;
310  // Set flag
311  fStopSignaled = true;
312 #ifdef HAVE_WINTHREAD
313  // Wait for thread to terminate itself
314  HANDLE hThread = reinterpret_cast<HANDLE>(iThread);
315  if (WaitForSingleObject(hThread, 10000) == WAIT_TIMEOUT)
316  // ... or kill it in case it refuses to do so
317  TerminateThread(hThread, -1);
318 #elif defined(HAVE_PTHREAD)
319  // wait for thread to terminate itself
320  // (whithout security - let's trust these unwashed hackers for once)
321  pthread_join(Thread, nullptr);
322 #endif
323  fStarted = false;
324  // ok
325  return;
326 }
327 
328 unsigned int StdThread::ThreadFunc()
329 {
330  // Keep calling Execute until someone gets fed up and calls Stop()
331  while (!IsStopSignaled())
332  Execute();
333  // Handle deletion
334  if (IsSelfDestruct())
335  {
336  fStarted = false; // reset start flag to avoid Stop() call
337  delete this;
338  }
339  return(0);
340 }
341 
343 {
344  return fStopSignaled;
345 }
virtual bool Execute(int iTimeout=-1, pollfd *readyfds=nullptr)=0
bool IsStopSignaled()
virtual C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow)
void Removing(StdSchedulerProc *pProc)
void Set(StdSchedulerProc **ppProcs, int iProcCnt)
C4TimeMilliseconds GetNextTick(C4TimeMilliseconds tNow)
void Remove(StdSchedulerProc *pProc)
void Add(StdSchedulerProc *pProc)
bool Start()
virtual bool IsSelfDestruct()
Definition: StdScheduler.h:338
bool ExecuteUntil(int iTimeout=-1)
void SignalStop()
~StdSchedulerThread() override
virtual void Execute()=0
void Remove(StdSchedulerProc *pProc)
void StartOnCurrentThread()
virtual ~StdScheduler()
bool ScheduleProcs(int iTimeout=1000/36)
virtual bool DoScheduleProcs(int iTimeout)
void Set(StdSchedulerProc **ppProcs, int iProcCnt)
void Added(StdSchedulerProc *pProc)
#define s
static C4TimeMilliseconds Now()
void Add(StdSchedulerProc *pProc)