KSquare Utilities
KKThread.cpp
Go to the documentation of this file.
1 /* KKThread.cpp -- Manages the threads that perform the image extraction process.
2  * Copyright (C) 2012-2014 Kurt Kramer
3  * For conditions of distribution and use, see copyright notice in KKB.h
4  */
5 #include "FirstIncludes.h"
6 #include <fstream>
7 #include <iostream>
8 #include <map>
9 #include <ostream>
10 #include <string>
11 #include <vector>
12 #include "MemoryDebug.h"
13 using namespace std;
14 
15 #include "KKBaseTypes.h"
16 
17 #if defined(WIN32)
18 #include <windows.h>
19 #else
20 #include <pthread.h>
21 #endif
22 
23 #include "KKException.h"
24 #include "MsgQueue.h"
25 #include "OSservices.h"
26 #include "KKThread.h"
27 using namespace KKB;
28 
29 
30 
31 #if defined(WIN32)
32 //
33 // Usage: SetThreadName (-1, "MainThread");
34 //
35 #define MS_VC_EXCEPTION 0x406D1388
36 
37 #pragma pack(push,8)
38 typedef struct tagTHREADNAME_INFO
39 {
40  DWORD dwType; /**< Must be 0x1000. */
41  LPCSTR szName; /**< Pointer to name (in user address space). */
42  DWORD dwThreadID; /**< Thread ID (-1=caller thread). */
43  DWORD dwFlags; /**< Reserved for future use, must be zero. */
44 } THREADNAME_INFO;
45 #pragma pack(pop)
46 #endif
47 
48 
49 
50 #if defined(WIN32)
51 
52 void KKThread::SetThreadName ()
53 {
54  Sleep(10);
55  THREADNAME_INFO info;
56  info.dwType = 0x1000;
57  info.szName = threadName.Str ();
58  info.dwThreadID = windowsThreadId;
59  info.dwFlags = 0;
60 
61 #if defined(_MSC_VER)
62  __try
63  {
64  RaiseException( MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info );
65  }
66  __except(EXCEPTION_EXECUTE_HANDLER)
67  {
68  }
69 #endif
70 
71 } /* SetThreadName */
72 #else
73 void KKThread::SetThreadName ()
74 {
75 
76 }
77 #endif
78 
79 
80 
81 
82 
83 KKThread::KKThread (const KKStr& _threadName,
84  KKThreadManagerPtr _threadManager,
85  MsgQueuePtr _msgQueue
86  ):
87  crashed (false),
88  msgQueue (_msgQueue),
89  priority (ThreadPriority::Normal),
90  shutdownFlag (false),
91  shutdownPrerequisites (NULL),
92  startPrerequisites (NULL),
93  status (ThreadStatus::NotStarted),
94  terminateFlag (false),
95  threadId (0),
96  threadName (_threadName)
97 
98 {
99 #if defined(WIN32)
100  windowsThreadHandle = NULL;
101  windowsThreadId = 0;
102 #else
103  linuxThreadId = 0;
104 #endif
105 }
106 
107 
108 
110 {
111  delete shutdownPrerequisites; shutdownPrerequisites = NULL;
112  delete startPrerequisites; startPrerequisites = NULL;
113 }
114 
115 
116 
118 {
119  kkint32 estMem = sizeof (crashed) +
120  sizeof (msgQueue) +
121  sizeof (shutdownFlag) +
122  sizeof (startPrerequisites) +
123  sizeof (shutdownPrerequisites) +
124  sizeof (status) +
125  sizeof (terminateFlag) +
126  threadName.MemoryConsumedEstimated ();
127 
128  if (msgQueue)
129  estMem += msgQueue->MemoryConsumedEstimated ();
130 
131  if (startPrerequisites)
132  estMem += startPrerequisites->MemoryConsumedEstimated ();
133 
134  if (shutdownPrerequisites)
135  estMem += shutdownPrerequisites->MemoryConsumedEstimated ();
136 
137  return estMem;
138 }
139 
140 
141 KKStr KKThread::threadStatusDescs[] = {"NULL", "Started", "Running", "Stopping", "Stopped"};
142 
143 
145 {
146  if ((ts < ThreadStatus::Null) || (ts > ThreadStatus::Stopped))
147  return KKStr::EmptyStr ();
148  else
149  return threadStatusDescs[(int)ts];
150 }
151 
152 
153 
154 void KKThread::AddMsg (KKStrPtr msg)
155 {
156  if (msgQueue == NULL)
157  {
158  cerr << endl
159  << "KKThread::AddMsg ***ERROR*** msgQuue is not defined." << endl
160  << " Msg[" << *msg << "]." << endl
161  << endl;
162  delete msg;
163  msg = NULL;
164  }
165  else
166  {
167  msgQueue->AddMsg (msg);
168  }
169 } /* AddMsg */
170 
171 
172 
173 
174 void KKThread::AddMsg (const KKStr& msg)
175 {
176  if (msgQueue == NULL)
177  {
178  cerr << endl
179  << "KKThread::AddMsg ***ERROR*** msgQuue is not defined." << endl
180  << " Msg[" << msg << "]." << endl
181  << endl;
182  }
183  else
184  {
185  KKStr msgTemp (msg.Len () + 20);
186  msgTemp << osGetThreadId () << " - " << osGetLocalDateTime ().Time () << "->" << msg;
187  msgQueue->AddMsg (msgTemp);
188  }
189 } /* AddMsg */
190 
191 
192 
193 KKStrListPtr KKThread::GetMsgs ()
194 {
195  KKStrListPtr results = new KKStrList (true);
196  KKStrPtr msg = msgQueue->GetNextMsg ();
197  while (msg)
198  {
199  results->PushOnBack (msg);
200  msg = msgQueue->GetNextMsg ();
201  }
202 
203  return results;
204 } /* GetMsgs */
205 
206 
207 
209 {
210  terminateFlag = true;
212 }
213 
214 
215 
217 {
218 }
219 
220 
222 {
223  return ((status == ThreadStatus::Running) ||
224  (status == ThreadStatus::Starting) ||
225  (status == ThreadStatus::Stopping)
226  );
227 }
228 
229 
230 
232 {
233  shutdownFlag = true;
234 }
235 
236 
237 
238 void KKThread::WaitForThreadToStop (kkuint32 maxTimeToWait)
239 {
240  if ((status == ThreadStatus::NotStarted) ||
241  (status == ThreadStatus::Stopped) ||
242  (status == ThreadStatus::Null)
243  )
244  {
245  // Thread is not running; we can return.
246  return;
247  }
248 
250  kkuint32 timeWaitedSoFar = 0;
251  while ((status == ThreadStatus::Running) || (status == ThreadStatus::Stopping))
252  {
254  if (maxTimeToWait > 0)
255  {
257  timeWaitedSoFar = (kkuint32)(now - startTime);
258  if (timeWaitedSoFar > maxTimeToWait)
259  break;
260  }
261  }
262 
263 
264  if ((status != ThreadStatus::NotStarted) &&
265  (status != ThreadStatus::Stopped) &&
266  (status != ThreadStatus::Null)
267  )
268  {
269  Kill ();
270  }
271 
272 } /* WaitForThreadToStop */
273 
274 
275 
276 
277 #if defined(WIN32)
278 extern "C"
279 {
280  unsigned int ThreadStartCallBack (void* param)
281  {
282  KKThreadPtr tp = (KKThreadPtr)param;
284  try
285  {
286  tp->Run ();
287  }
288  catch (const KKException& e1)
289  {
290  tp->Crashed (true);
292  }
293  catch (const std::exception e2)
294  {
295  tp->Crashed (true);
296  const char* e2What = e2.what ();
297  KKStr msg (30 + strlen (e2What));
298  msg << "std::exception: " << e2What;
299  tp->ExceptionText (msg);
300  }
301  catch (...)
302  {
303  tp->Crashed (true);
304  tp->ExceptionText ("exception(...) trapped.");
305  }
306 
308  return 0;
309  }
310 }
311 #else
312 
313 
314 void* ThreadStartCallBack (void* param)
315 {
316  KKThreadPtr tp = (KKThreadPtr)param;
317  tp->Status (KKThread::Starting);
318  try
319  {
320  tp->Run ();
321  }
322  catch (const KKException& e1)
323  {
324  tp->Crashed (true);
325  tp->ExceptionText (e1.ToString ());
326  }
327  catch (const std::exception e2)
328  {
329  tp->Crashed (true);
330  const char* e2What = e2.what ();
331  KKStr msg (30 + strlen (e2What));
332  msg << "std::exception: " << e2What;
333  tp->ExceptionText (msg);
334  }
335  catch (...)
336  {
337  tp->Crashed (true);
338  const char* e2What = e2.what ();
339  tp->ExceptionText ("exception(...) trapped.");
340  }
341  tp->Status (KKThread::Stopped);
342  return 0;
343 }
344 
345 
346 #endif
347 
348 
349 
350 #if defined(WIN32)
351 
352 void KKThread::Start (ThreadPriority _priority,
353  bool& successful
354  )
355 {
356  priority = _priority;
357 
358  windowsThreadHandle = (unsigned long*)CreateThread (NULL,
359  0,
360  (LPTHREAD_START_ROUTINE)ThreadStartCallBack,
361  (KKThreadPtr)this,
362  0,
363  &windowsThreadId
364  );
365  if (windowsThreadHandle == NULL)
366  {
367  successful = false;
368  throw KKException ("Failed to create thread");
369  }
370  else
371  {
372  threadId = (kkint32)windowsThreadId;
373 
374  switch (priority)
375  {
376  case ThreadPriority::Null:
377  case ThreadPriority::Normal:
378  SetThreadPriority (windowsThreadHandle, THREAD_PRIORITY_BELOW_NORMAL);
379  break;
380 
381  case ThreadPriority::Low:
382  SetThreadPriority (windowsThreadHandle, THREAD_PRIORITY_NORMAL);
383  break;
384 
385  case ThreadPriority::High:
386  SetThreadPriority (windowsThreadHandle, THREAD_PRIORITY_ABOVE_NORMAL);
387  break;
388  }
389  SetThreadName ();
390  successful = true;
391  }
392 } /* Start */
393 
394 #else
395 
396 void KKThread::Start (ThreadPriority _priority,
397  bool& successful
398  )
399 {
400  int returnCd = pthread_create (&linuxThreadId,
401  NULL, // const pthread_attr_t * attr,
402  ThreadStartCallBack, // void * (*start_routine)(void *),
403  (void*)this
404  );
405  if (returnCd != 0)
406  {
407  successful = false;
408  throw KKException ("Failed to create thread");
409  }
410  else
411  {
412  threadId = (kkint32)linuxThreadId;
413  SetThreadName ();
414  successful = true;
415  }
416 } /* Start */
417 
418 #endif
419 
420 
421 
422 /**
423  *@brief stops the running thread and frees the thread handle
424  **/
425 
426 #if defined(WIN32)
427 void KKThread::Kill ()
428 {
429  if (windowsThreadHandle == NULL)
430  return;
431 
432  WaitForSingleObject (windowsThreadHandle, INFINITE);
433  CloseHandle (windowsThreadHandle);
434  windowsThreadHandle = NULL;
435 } /* Kill */
436 #else
437 void KKThread::Kill ()
438 {
439  pthread_cancel (linuxThreadId);
440 } /* Kill */
441 #endif
442 
443 
444 
445 
446 
447 void KKThread::Run ()
448 {
449  // This method should have been over ridden by a derived class.
450  const char* msg = "KKThread::Run ***ERROR*** This method should have been over ridden by a derived class.";
451 
452  cerr << endl << endl << msg << endl
453  << endl;
454  AddMsg (msg);
455 }
456 
457 
458 
460 {
461  if (!startPrerequisites)
462  return false;
463 
464  KKThreadList::const_iterator idx;
465  for (idx = startPrerequisites->begin (); idx != startPrerequisites->end (); ++idx)
466  {
467  KKThreadPtr preReq = *idx;
468  if (preReq == _thread)
469  return true;
470 
471  else if (preReq->ThereIsACircularReferenceStart (_thread))
472  return true;
473  }
474  return false;
475 } /* ThereIsACircularReferenceStart */
476 
477 
478 
479 
481 {
482  if (!shutdownPrerequisites)
483  return false;
484 
485  KKThreadList::const_iterator idx;
486  for (idx = shutdownPrerequisites->begin (); idx != shutdownPrerequisites->end (); ++idx)
487  {
488  KKThreadPtr preReq = *idx;
489  if (preReq == _thread)
490  return true;
491 
492  else if (preReq->ThereIsACircularReferenceStart (_thread))
493  return true;
494  }
495  return false;
496 } /* ThereIsACircularReferenceShutdown */
497 
498 
499 
500 
502 {
503  if (!startPrerequisites)
504  startPrerequisites = new KKThreadList (false);
505  startPrerequisites->PushOnBack (_thread);
506 }
507 
508 
509 
511 {
512  if (!shutdownPrerequisites)
513  shutdownPrerequisites = new KKThreadList (false);
514  shutdownPrerequisites->PushOnBack (_thread);
515 }
516 
517 
518 
519 bool KKThread::OkToShutdown () const
520 {
521  if (!shutdownPrerequisites)
522  return true;
523 
524  KKThreadList::const_iterator idx;
525  for (idx = shutdownPrerequisites->begin (); idx != shutdownPrerequisites->end (); ++idx)
526  {
527  KKThreadPtr preReq = *idx;
528  if (preReq->Status () != ThreadStatus::Stopped)
529  return false;
530  }
531  return true;
532 } /* OkToShutdown */
533 
534 
535 
536 bool KKThread::OkToStart () const
537 {
538  if (!startPrerequisites)
539  return true;
540 
541  KKThreadList::const_iterator idx;
542  for (idx = startPrerequisites->begin (); idx != startPrerequisites->end (); ++idx)
543  {
544  KKThreadPtr preReq = *idx;
545  if (preReq->Status () != ThreadStatus::Running)
546  return false;
547  }
548  return true;
549 } /* OkToStart */
550 
551 
552 
553 
554 
555 KKThreadList::KKThreadList (bool _owner):
557 {
558 }
559 
560 
561 
564 {
565 }
566 
567 
568 
570 {
571 }
572 
573 
574 
576 {
577  kkint32 memEst = sizeof (KKThreadList);
578 
579  KKThreadList::const_iterator idx;
580  for (idx = begin (); idx != end (); ++idx)
581  {
582  KKThreadPtr t = *idx;
583  memEst += t->MemoryConsumedEstimated ();
584  }
585  return memEst;
586 }
void AddMsg(KKStrPtr msg)
Take ownership of &#39;msg&#39; and add to end of the queue.
Definition: MsgQueue.cpp:58
KKStr(kkint32 size)
Creates a KKStr object that pre-allocates space for &#39;size&#39; characters.
Definition: KKStr.cpp:655
void Start(ThreadPriority _priority, bool &successful)
Definition: KKThread.cpp:352
bool ThreadStillProcessing() const
Definition: KKThread.cpp:221
KKThreadList(const KKThreadList &list)
Definition: KKThread.cpp:562
ThreadStatus Status() const
Definition: KKThread.h:72
kkint32 MemoryConsumedEstimated() const
Definition: KKStr.cpp:766
__int32 kkint32
Definition: KKBaseTypes.h:88
KKThread * KKThreadPtr
Definition: KKThread.h:30
KKB::DateTime osGetLocalDateTime()
Returned the current local date and time.
KKThread(const KKStr &_threadName, KKThreadManagerPtr _threadManager, MsgQueuePtr _msgQueue)
Definition: KKThread.cpp:83
kkint32 MemoryConsumedEstimated()
Returns an estimate of the amount of memory consumed in bytes by this instance.
Definition: MsgQueue.cpp:146
void AddMsg(const KKStr &msg)
Definition: MsgQueue.cpp:76
virtual ~KKThread()
Definition: KKThread.cpp:109
bool ThereIsACircularReferenceShutdown(KKThreadPtr _thread) const
Definition: KKThread.cpp:480
kkint32 osGetThreadId()
void AddStartPrerequistite(KKThreadPtr _thread)
Specify threads that must start before this thread is started.
Definition: KKThread.cpp:501
#define MS_VC_EXCEPTION
Definition: KKThread.cpp:35
unsigned __int32 kkuint32
Definition: KKBaseTypes.h:89
kkint32 MemoryConsumedEstimated() const
Definition: KKThread.cpp:575
virtual void TerminateFlagChanged()
Will be called whenever the value of &#39;terminateFlag&#39; is changed; derived classes should override this...
Definition: KKThread.cpp:216
KKStrPtr GetNextMsg()
Removes from the queue the oldest message added to the queue that has not been removed.
Definition: MsgQueue.cpp:107
kkuint32 Len() const
Returns the number of characters in the string.
Definition: KKStr.h:366
KKTHread * KKTHreadPtr
void TerminateThread()
Definition: KKThread.cpp:208
KKThreadManager * KKThreadManagerPtr
Definition: KKThread.h:15
void ExceptionText(const KKStr &_exceptionText)
Definition: KKThread.h:90
KKStr(const KKStr &str)
Copy Constructor.
Definition: KKStr.cpp:561
KKStrListPtr GetMsgs()
Definition: KKThread.cpp:193
The base class to be used any thread.
Definition: KKThread.h:27
bool OkToShutdown() const
Definition: KKThread.cpp:519
static KKStr Concat(const std::vector< std::string > &values)
Concatenates the list of &#39;std::string&#39; strings.
Definition: KKStr.cpp:1082
void AddShutdownPrerequistite(KKThreadPtr _thread)
Specify threads that must stop before this thread is started.
Definition: KKThread.cpp:510
void osSleepMiliSecs(kkuint32 numMiliSecs)
static const KKStr & EmptyStr()
Static method that returns an Empty String.
Definition: KKStr.cpp:3453
bool ThereIsACircularReferenceStart(KKThreadPtr _thread) const
Definition: KKThread.cpp:459
void Status(ThreadStatus _status)
Definition: KKThread.h:91
kkint32 MemoryConsumedEstimated()
Definition: KKThread.cpp:117
unsigned __int64 kkuint64
Definition: KKBaseTypes.h:91
void WaitForThreadToStop(kkuint32 maxTimeToWait)
Called by separate thread; will stay in loop until the thread controlled by this instance shutdown or...
Definition: KKThread.cpp:238
KKThreadList(bool _owner=true)
Definition: KKThread.cpp:555
kkuint64 Seconds() const
Definition: DateTime.cpp:1092
KKStrList(bool owner)
Definition: KKStr.cpp:4485
KKException(const char *_exceptionStr)
Definition: KKException.cpp:38
void Kill()
stops the running thread and frees the thread handle
Definition: KKThread.cpp:427
void ShutdownThread()
Definition: KKThread.cpp:231
void AddMsg(KKStrPtr msg)
Definition: KKThread.cpp:154
unsigned int ThreadStartCallBack(void *param)
Definition: KKThread.cpp:280
static const KKStr & ThreadStatusToStr(ThreadStatus)
Definition: KKThread.cpp:144
void Crashed(bool _crashed)
Definition: KKThread.h:89
virtual const KKStr & ToString() const
Definition: KKException.cpp:98
virtual void Run()
Definition: KKThread.cpp:447
bool OkToStart() const
Definition: KKThread.cpp:536
void AddMsg(const KKStr &msg)
Definition: KKThread.cpp:174
const TimeType & Time() const
Definition: DateTime.h:232