KSquare Utilities
GoalKeeper.cpp
Go to the documentation of this file.
1 /* GoalKeeper.cpp -- Implements blocking routines to support thread synchronization.
2  * Copyright (C) 1994-2011 Kurt Kramer
3  * For conditions of distribution and use, see copyright notice in KKB.h
4  */
5 #include "FirstIncludes.h"
6 
7 #include <errno.h>
8 #include <istream>
9 #include <iostream>
10 #include <fstream>
11 //#include <stdio.h>
12 #include <vector>
13 
14 #include "KKBaseTypes.h"
15 
16 
17 #if defined(WIN32)
18 #include <windows.h>
19 #else
20 #include <fcntl.h>
21 #include <semaphore.h>
22 #endif
23 #include "MemoryDebug.h"
24 
25 using namespace std;
26 
27 #include "KKException.h"
28 #include "GoalKeeper.h"
29 #include "OSservices.h"
30 using namespace KKB;
31 
32 
33 
34 
35 #if defined(GOALKEEPER_DEBUG)
36 class GoalKeeper::BlockedStat
37 {
38 public:
39  BlockedStat (kkint32 _blockedThreadId,
40  kkint32 _blockerThreadId,
41  kkuint32 _milliSecsBlocked,
42  kkint32 _numBlockedThreads
43  ):
44  blockedThreadId (_blockedThreadId),
45  blockerThreadId (_blockerThreadId),
46  milliSecsBlocked (_milliSecsBlocked),
47  numBlockedThreads (_numBlockedThreads),
48  dateTimeBlocked (osGetLocalDateTime ())
49  {
50  }
51 
52  kkint32 blockedThreadId; /**< Thread Being Blocked. */
53  kkint32 blockerThreadId; /**< Thread holding Block. */
54  kkuint32 milliSecsBlocked;
55  kkint32 numBlockedThreads;
56  DateTime dateTimeBlocked;
57 };
58 
59 
60 class GoalKeeper::BlockedStatList: public KKQueue<BlockedStat>
61 {
62 public:
63  BlockedStatList (bool _owner): KKQueue<BlockedStat> (_owner) {}
64 };
65 
66 
67 #endif
68 
69 
70 GoalKeeperListPtr GoalKeeper::existingGoalKeepers = NULL;
71 
72 
73 
74 GoalKeeper::GoalKeeper (const KKStr& _name):
75  blocked (false),
76  blockerDepth (0),
77  blockerThreadId (-1),
78  name (_name),
79  numBlockedThreads (0)
80 {
81 #if defined(WIN32)
82  InitializeCriticalSection (&cs);
83 #else
84  pthread_mutex_init (&mutex, NULL);
85 #endif
86 
87 #if defined(GOALKEEPER_DEBUG)
88  blockedStats = new BlockedStatList (true);
89 #endif
90 }
91 
92 
93 
94 GoalKeeper::~GoalKeeper ()
95 {
96 #if defined(WIN32)
97  if (blocked)
98  CriticalSectionEnd ();
99 #else
100  pthread_mutex_destroy (&mutex);
101 #endif
102 
103 #if defined(GOALKEEPER_DEBUG)
104  ReportBlockedStats ();
105  delete blockedStats;
106  blockedStats = NULL;
107 #endif
108 
109 }
110 
111 
112 
113 
114 
115 #if defined(GOALKEEPER_DEBUG)
116 void GoalKeeper::ReportBlockedStats ()
117 {
118  HANDLE mutexCreateHandle = CreateMutex (NULL, /**< default security attributes. */
119  false, /**< initially not owned. */
120  "GoalKeeper_ReportBlockedStats"
121  );
122 
123  WaitForSingleObject (mutexCreateHandle, INFINITE);
124 
125  KKStr path = "C:\\Temp\\BlockedStats";
126 
127  KKB::osCreateDirectoryPath (path);
128 
129  KKStr fileName = osAddSlash (path) + "BlockedStats.txt";
130 
131  ofstream o (fileName.Str (), ios_base::app);
132 
133  o << "Name"
134  << "\t" << "Count"
135  << "\t" << "BlockedThreadId"
136  << "\t" << "BlockerThreadId"
137  << "\t" << "DateTimeBlocked"
138  << "\t" << "MilliSecsBlocked"
139  << "\t" << "NumBlockedThreads"
140  << endl;
141 
142  int c = 0;
143  BlockedStatList::iterator idx;
144  for (idx = blockedStats->begin (), c = 0; idx != blockedStats->end (); ++idx, ++c)
145  {
146  BlockedStat* bs = *idx;
147  o << name
148  << "\t" << c
149  << "\t" << bs->blockedThreadId
150  << "\t" << bs->blockerThreadId
151  << "\t" << bs->dateTimeBlocked.Date () << " " << bs->dateTimeBlocked.Time ()
152  << "\t" << bs->milliSecsBlocked
153  << "\t" << bs->numBlockedThreads
154  << endl;
155  }
156 
157  o.close ();
158 
159  ReleaseMutex (mutexCreateHandle);
160  CloseHandle(mutexCreateHandle);
161 } /* ReportBlockedStats */
162 #endif
163 
164 
165 
167 {
168  return (sizeof (GoalKeeper) + name.MemoryConsumedEstimated ());
169 }
170 
171 
172 
174 {
175  return blocked;
176 } /* Blocked */
177 
178 
179 
181 {
182  if (!blocked)
183  return false;
184 
185  kkint32 curThreadId = KKB::osGetThreadId ();
186  return (blocked && (curThreadId != blockerThreadId));
187 }
188 
189 
190 
191 void GoalKeeper::CriticalSectionStart ()
192 {
193 #if defined(WIN32)
194  EnterCriticalSection (&cs);
195 #else
196  pthread_mutex_lock (&mutex);
197 #endif
198 }
199 
200 
201 
202 
203 void GoalKeeper::CriticalSectionEnd ()
204 {
205 #if defined(WIN32)
206  LeaveCriticalSection (&cs);
207 #else
208  pthread_mutex_unlock (&mutex);
209 #endif
210 }
211 
212 
213 
215 {
216  kkint32 curThreadId = KKB::osGetThreadId ();
217 
218  bool firstPassThruLoop = true;
219  bool weAreBlocked = true;
220  kkuint32 milliSecsBlocked = 0;
221 
222  while (weAreBlocked)
223  {
224  CriticalSectionStart ();
225 
226  if (firstPassThruLoop)
227  numBlockedThreads++;
228 
229  if (blocked)
230  {
231  if (curThreadId == blockerThreadId)
232  {
233  // We are the thread that already holds the block; so okay for us
234  // to process.
235  blockerDepth++;
236  weAreBlocked = false;
237  numBlockedThreads--;
238  }
239  else
240  {
241  weAreBlocked = true;
242 #if defined(GOALKEEPER_DEBUG)
243  blockedStats->PushOnBack (new BlockedStat (curThreadId, blockerThreadId, milliSecsBlocked, numBlockedThreads));
244 #endif
245  }
246  }
247  else
248  {
249  // No one holds the lock; so we can take it.
250  blocked = true;
251  blockerDepth = 1;
252  blockerThreadId = curThreadId;
253  weAreBlocked = false;
254  numBlockedThreads--;
255  }
256 
257  CriticalSectionEnd ();
258 
259  if (weAreBlocked)
260  {
261  if (milliSecsBlocked < 10)
262  {
264  milliSecsBlocked++;
265  }
266 
267  else if (milliSecsBlocked < 200)
268  {
270  milliSecsBlocked += 10;
271  }
272 
273  else if (milliSecsBlocked < 10000)
274  {
275  osSleepMiliSecs (100);
276  milliSecsBlocked += 100;
277  }
278 
279  else
280  {
281  osSleepMiliSecs (400);
282  milliSecsBlocked += 400;
283  }
284  }
285 
286  firstPassThruLoop = false;
287  }
288 
289  return;
290 } /* StartBlock */
291 
292 
293 
294 
296 {
297  kkint32 curProcessorId = KKB::osGetThreadId ();
298 
299  kkint32 errorCode = 0; // 0=No Error;
300  // 1=There is no Block
301  // 2=Different thread holds the block
302  // 3=Failure to get a lock
303 
304  {
305  CriticalSectionStart ();
306 
307  if (!blocked)
308  {
309  errorCode = 1;
310  }
311 
312  else if (curProcessorId != blockerThreadId)
313  {
314  errorCode = 2;
315  }
316 
317  else
318  {
319  blockerDepth--;
320  if (blockerDepth < 1)
321  {
322  blocked = false;
323  blockerThreadId = -1;
324  blockerDepth = 0;
325  }
326  }
327 
328  CriticalSectionEnd ();
329  }
330 
331  if (errorCode == 0)
332  return;
333 
334  else if (errorCode == 1)
335  throw KKStr ("GoalKeeper::EndBlock Name[" + name + "] There was no block established.");
336 
337  else if (errorCode == 2)
338  throw KKStr ("GoalKeeper::EndBlock Name[" + name + "] ThreadId[" + curProcessorId + "] Currently holds Block; our ThreadId[" + curProcessorId + "]");
339 
340  return;
341 } /* EndBlock */
342 
343 
344 
345 
346 void GoalKeeper::Create (const KKStr& _name,
347  volatile GoalKeeperPtr& _newGoalKeeper
348  )
349 {
350 #if defined(WIN32)
351  HANDLE mutexCreateHandle = CreateMutex (NULL, /**< default security attributes */
352  false, /**< initially not owned */
353  "GoalKeeperClass"
354  );
355  if (mutexCreateHandle == NULL)
356  throw KKException ("GoalKeeper::Create failed to get handle to Mutex Object 'GoalKeeperClass'.");
357 
358  WaitForSingleObject (mutexCreateHandle, INFINITE);
359 
360  if (_newGoalKeeper == NULL)
361  {
362  _newGoalKeeper = new GoalKeeper (_name);
363  if (existingGoalKeepers == NULL)
364  {
365  existingGoalKeepers = new GoalKeeperList (true);
366  atexit (GoalKeeper::FinalCleanUp);
367  }
368  existingGoalKeepers->PushOnBack (_newGoalKeeper);
369  }
370 
371  ReleaseMutex (mutexCreateHandle);
372  CloseHandle(mutexCreateHandle);
373 #else
374  sem_t* semHandle = sem_open ("GoalKeeperClass", O_CREAT, 0644, 1);
375  if (semHandle == SEM_FAILED)
376  {
377  cout << std::endl
378  << "GoalKeeper::Create Error[" << errno << "] opening '/GoalKeeper' Semaphore." << std::endl
379  << std::endl;
380 
381  perror("GoalKeeper::Create Error Opening Semaphore 'GoalKeeper'");
382 
383  throw "GoalKeeper::Create Error opening 'GoalKeeper'.";
384  }
385 
386  sem_wait (semHandle);
387 
388  if (_newGoalKeeper == NULL)
389  {
390  _newGoalKeeper = new GoalKeeper (_name);
391  if (existingGoalKeepers == NULL)
392  {
393  existingGoalKeepers = new GoalKeeperList (true);
394  atexit (GoalKeeper::FinalCleanUp);
395  }
396  existingGoalKeepers->PushOnBack (_newGoalKeeper);
397  }
398 
399  sem_post (semHandle);
400  sem_close (semHandle);
401 #endif
402 } /* Create */
403 
404 
405 
406 
407 /**
408  *@brief Create a new instance of a GoalKeeper object if it has not already been done and locks it if we create it.
409  *@param[in] _name Name to be assigned to GoalKeeper object.
410  *@param[in,out] _newGoalKeeper A pointer to the GoalKeeper that already exists or to new one that got created.
411  *@param[out] _didNotExistYet Indicates if the call to 'CreateAndStartBlock' had to create a new instance.
412  */
414  volatile GoalKeeperPtr& _newGoalKeeper,
415  bool& _didNotExistYet
416  )
417 {
418 #if defined(WIN32)
419  HANDLE mutexCreateHandle = CreateMutex (NULL, /**< default security attributes. */
420  false, /**< initially not owned. */
421  "GoalKeeperClass"
422  );
423  if (mutexCreateHandle == NULL)
424  throw KKException("GoalKeeper::CreateAndStartBlock failed to get handle to Mutex Object 'GoalKeeperClass'.");
425 
426  WaitForSingleObject (mutexCreateHandle, INFINITE);
427 
428  if (_newGoalKeeper == NULL)
429  {
430  _didNotExistYet = true;
431  _newGoalKeeper = new GoalKeeper (_name);
432 
433  if (existingGoalKeepers == NULL)
434  {
435  existingGoalKeepers = new GoalKeeperList (true);
436  atexit (GoalKeeper::FinalCleanUp);
437  }
438  existingGoalKeepers->PushOnBack (_newGoalKeeper);
439  }
440  else
441  {
442  _didNotExistYet = false;
443  }
444 
445  _newGoalKeeper->StartBlock ();
446 
447  ReleaseMutex (mutexCreateHandle);
448  CloseHandle(mutexCreateHandle);
449 #else
450  sem_t* semHandle = sem_open ("GoalKeeperClass", O_CREAT, 0644, 1);
451  if (semHandle == SEM_FAILED)
452  {
453  cout << std::endl
454  << "GoalKeeper::Create Error[" << errno << "] opening '/GoalKeeper' Semaphore." << std::endl
455  << std::endl;
456 
457  perror("GoalKeeper::Create Error Opening Semaphore 'GoalKeeper'");
458 
459  throw "GoalKeeper::Create Error opening 'GoalKeeper'.";
460  }
461 
462  sem_wait (semHandle);
463 
464  if (_newGoalKeeper == NULL)
465  {
466  _didNotExistYet = true;
467  _newGoalKeeper = new GoalKeeper (_name);
468  if (existingGoalKeepers == NULL)
469  {
470  existingGoalKeepers = new GoalKeeperList (true);
471  atexit (GoalKeeper::FinalCleanUp);
472  }
473  existingGoalKeepers->PushOnBack (_newGoalKeeper);
474  }
475  else
476  {
477  _didNotExistYet = false;
478  }
479 
480  _newGoalKeeper->StartBlock ();
481 
482  sem_post (semHandle);
483  sem_close (semHandle);
484 #endif
485 } /* CreateAndStartBlock */
486 
487 
488 
489 
490 
491 void GoalKeeper::Destroy (volatile GoalKeeperPtr& _goalKeeperInstance)
492 {
493 #if defined(WIN32)
494  HANDLE mutexCreateHandle = CreateMutex (NULL, /**< default security attributes */
495  false, /**< initially not owned */
496  "GoalKeeperClass"
497  );
498  if (mutexCreateHandle == NULL)
499  throw KKException("GoalKeeper::Destroy failed to get handle to Mutex Object 'GoalKeeperClass'.");
500 
501  WaitForSingleObject (mutexCreateHandle, INFINITE);
502 
503  if (_goalKeeperInstance == NULL)
504  {
505  // Some other thread managed to destroy this instance.
506  }
507  else
508  {
509  kkint32 existingInstanceIdx = existingGoalKeepers->PtrToIdx (_goalKeeperInstance);
510  if (existingInstanceIdx < 0)
511  {
512  // If not in list then a different thread beat us to destroying this instance or it was never created to start with.
513  }
514  else
515  {
516  existingGoalKeepers->DeleteEntry (_goalKeeperInstance);
517  delete _goalKeeperInstance;
518  _goalKeeperInstance = NULL;
519  }
520  }
521 
522  ReleaseMutex (mutexCreateHandle);
523  CloseHandle(mutexCreateHandle);
524 #else
525  sem_t* semHandle = sem_open ("GoalKeeperClass", O_CREAT, 0644, 1);
526  if (semHandle == SEM_FAILED)
527  {
528  cout << std::endl
529  << "GoalKeeper::Create Error[" << errno << "] opening '/GoalKeeper' Semaphore." << std::endl
530  << std::endl;
531 
532  perror("GoalKeeper::Create Error Opening Semaphore 'GoalKeeper'");
533 
534  throw "GoalKeeper::Create Error opening 'GoalKeeper'.";
535  }
536 
537  sem_wait (semHandle);
538 
539  if (_goalKeeperInstance == NULL)
540  {
541  // Some other thread managed to destroy this instance.
542  }
543  else
544  {
545  kkint32 existingInstanceIdx = existingGoalKeepers->PtrToIdx (_goalKeeperInstance);
546  if (existingInstanceIdx >= 0)
547  {
548  // If not in list then a different thread beat us to destroying this instance or it was never created to start with.
549  }
550  else
551  {
552  existingGoalKeepers->DeleteEntry (_goalKeeperInstance);
553  delete _goalKeeperInstance;
554  _goalKeeperInstance = NULL;
555  }
556  }
557 
558  sem_post (semHandle);
559  sem_close (semHandle);
560 #endif
561 
562 } /* Destroy */
563 
564 
565 
567 {
568  if (existingGoalKeepers)
569  {
570  delete existingGoalKeepers;
571  existingGoalKeepers = NULL;
572  }
573 }
574 
575 
576 
577 
578 
580 {
581  kkint32 x = 0;
582  x = numBlockedThreads;
583  return x;
584 }
585 
586 
587 
589 {
590  kkint32 x = 0;
591  x = blockerThreadId;
592  return x;
593 }
GoalKeeper * GoalKeeperPtr
Definition: GoalKeeper.h:41
kkint32 MemoryConsumedEstimated() const
Definition: KKStr.cpp:766
__int32 kkint32
Definition: KKBaseTypes.h:88
kkint32 BlockerThreadId()
ThreadId of thread that currently holds the Block -1 indicates no Block.
Definition: GoalKeeper.cpp:588
KKStr operator+(kkint32 right) const
Definition: KKStr.cpp:4036
static void CreateAndStartBlock(const KKStr &_name, volatile GoalKeeperPtr &_newGoalKeeper, bool &_didNotExistYet)
Create a GoalKeeper object and avoid a race condition doing it.
Definition: GoalKeeper.cpp:413
static void Destroy(volatile GoalKeeperPtr &_goalKeeperInstance)
Destroys an existing instance of GoalKeeper.
Definition: GoalKeeper.cpp:491
kkint32 osGetThreadId()
kkint32 MemoryConsumedEstimated() const
Definition: GoalKeeper.cpp:166
bool Blocked()
Will return true if any thread lock on this instance of "GoalKeeper".
Definition: GoalKeeper.cpp:173
static void Create(const KKStr &_name, volatile GoalKeeperPtr &_newGoalKeeper)
Create a GoalKeeper object and avoid a race condition doing it.
Definition: GoalKeeper.cpp:346
KKStr operator+(const char *right) const
Definition: KKStr.cpp:3986
unsigned __int32 kkuint32
Definition: KKBaseTypes.h:89
GoalKeeperList(bool _owner)
Definition: GoalKeeper.h:198
KKTHread * KKTHreadPtr
KKStr operator+(const char *left, const KKStr &right)
Definition: KKStr.cpp:3976
KKStr(const KKStr &str)
Copy Constructor.
Definition: KKStr.cpp:561
void StartBlock()
Initiates a Block as long as another thread has not already locked this object.
Definition: GoalKeeper.cpp:214
void EndBlock()
Ends the block and allows other threads to pass through StatBlock.
Definition: GoalKeeper.cpp:295
static KKStr Concat(const std::vector< std::string > &values)
Concatenates the list of &#39;std::string&#39; strings.
Definition: KKStr.cpp:1082
void osSleepMiliSecs(kkuint32 numMiliSecs)
static void FinalCleanUp()
Will be registered with &#39;atexit&#39; so that it will be called when program is unloaded from memory...
Definition: GoalKeeper.cpp:566
kkint32 NumBlockedThreads()
Returns the number of threads that are waiting to establish a lock on this instance.
Definition: GoalKeeper.cpp:579
GoalKeeperList * GoalKeeperListPtr
Definition: GoalKeeper.h:23
bool BlockedByAnotherThread()
Returns true if a different thread has this instance of "GoalKeeper" locked.
Definition: GoalKeeper.cpp:180