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