1 /*
2  * main.cxx
3  *
4  * PWLib application source file for safetest
5  *
6  * Main program entry point.
7  *
8  * Copyright (c) 2006 Indranet Technologies Ltd
9  *
10  * The contents of this file are subject to the Mozilla Public License
11  * Version 1.0 (the "License"); you may not use this file except in
12  * compliance with the License. You may obtain a copy of the License at
13  * http://www.mozilla.org/MPL/
14  *
15  * Software distributed under the License is distributed on an "AS IS"
16  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17  * the License for the specific language governing rights and limitations
18  * under the License.
19  *
20  * The Original Code is Portable Windows Library.
21  *
22  * The Initial Developer of the Original Code is Indranet Technologies Ltd.
23  *
24  * Contributor(s): ______________________________________.
25  *
26  * $Revision: 20385 $
27  * $Author: rjongbloed $
28  * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
29  */
30 
31 
32 #ifdef P_USE_PRAGMA
33 #pragma implementation "main.h"
34 #endif
35 
36 
37 #include "precompile.h"
38 #include "main.h"
39 #include "version.h"
40 
41 PCREATE_PROCESS(SafeTest);
42 
43 #include  <ptclib/dtmf.h>
44 #include  <ptclib/random.h>
45 
46 
47 
SafeTest()48 SafeTest::SafeTest()
49   : PProcess("Derek Smithies code factory", "safetest", MAJOR_VERSION, MINOR_VERSION, BUILD_TYPE, BUILD_NUMBER)
50 {
51 }
52 
Main()53 void SafeTest::Main()
54 {
55   PArgList & args = GetArguments();
56 
57   args.Parse(
58              "h-help."               "-no-help."
59              "d-delay:"              "-no-delay."
60 	     "c-count:"              "-no-count."
61 	     "r-reporting."
62 	     "b-banpthreadcreate."
63 	     "a-alternate."
64 #if PTRACING
65              "o-output:"             "-no-output."
66              "t-trace."              "-no-trace."
67 #endif
68              "v-version."
69   );
70 
71 #if PTRACING
72   PTrace::Initialise(args.GetOptionCount('t'),
73                      args.HasOption('o') ? (const char *)args.GetOptionString('o') : NULL,
74          PTrace::Blocks | PTrace::Timestamp | PTrace::Thread | PTrace::FileAndLine);
75 #endif
76 
77   if (args.HasOption('v')) {
78     cout << "Product Name: " << GetName() << endl
79          << "Manufacturer: " << GetManufacturer() << endl
80          << "Version     : " << GetVersion(PTrue) << endl
81          << "System      : " << GetOSName() << '-'
82          << GetOSHardware() << ' '
83          << GetOSVersion() << endl;
84     return;
85   }
86 
87   if (args.HasOption('h')) {
88     PError << "Available options are: " << endl
89 	   << "-a                    Use a non opal end DelayThread mechanism" << endl
90 	   << "-b                    Avoid the usage of PThread::Create" << endl
91            << "-h  or --help         print this help" << endl
92 	   << "-r                    print reporting (every minute) on current statistics" << endl
93            << "-v  or --version      print version info" << endl
94            << "-d  or --delay ##     where ## specifies how many milliseconds the created thread waits for" << endl
95 	   << "-c  or --count ##     where ## specifies the number of active threads allowed " << endl
96 #if PTRACING
97            << "o-output              output file name for trace" << endl
98            << "t-trace.              trace level to use." << endl
99 #endif
100            << endl
101            << endl << endl;
102     return;
103   }
104 
105   delay = 2000;
106   if (args.HasOption('d'))
107     delay = args.GetOptionString('d').AsInteger();
108 
109   delay = PMIN(1000000, PMAX(1, delay));
110   cout << "Created thread will wait for " << delay << " milliseconds before ending" << endl;
111 
112   useOnThreadEnd = args.HasOption('a');
113   if (useOnThreadEnd)
114     cout << "Will use classes from the author to handle the end of a DelayThread instance" << endl;
115   else
116     cout << "Use methods similar to those in opal to handle the end of a DelayThread instance" << endl;
117 
118   avoidPThreadCreate = args.HasOption('b');
119   if (avoidPThreadCreate)
120     cout << "Will never ever call PThread::Create" << endl;
121   else
122     cout << "Will use PThread::Create in preference is classes written by the author"  << endl;
123 
124   regularReporting = args.HasOption('r');
125   if (regularReporting)
126     cout << "Will generate reports every minute on current status " << endl;
127   else
128     cout << "will keep silent about progress" << endl;
129 
130   activeCount = 10;
131   if (args.HasOption('c'))
132     activeCount = args.GetOptionString('c').AsInteger();
133   activeCount = PMIN(100, PMAX(1, activeCount));
134   cout << "There will be " << activeCount << " threads in operation" << endl;
135 
136   delayThreadsActive.SetAutoDeleteObjects();
137 
138   UserInterfaceThread ui(*this);
139   ui.Resume();
140   ui.WaitForTermination();
141 
142   exitNow = PTrue;
143 
144   cerr << "in preexit delay, let all threads die" << endl;
145   PThread::Sleep(delay * 2);
146 }
147 
OnReleased(DelayThread & delayThread)148 void SafeTest::OnReleased(DelayThread & delayThread)
149 {
150   PString id = delayThread.GetId();
151   PTRACE(3, "DelayThread " << id << " OnRelease");
152   delayThreadsActive.RemoveAt(id);
153   PTRACE(3, "DelayThread " << id << " OnRelease all done");
154   --currentSize;
155 }
156 
OnReleased(const PString & delayThreadId)157 void SafeTest::OnReleased(const PString & delayThreadId)
158 {
159   PTRACE(3, "DelayThread " << delayThreadId << " OnRelease");
160   delayThreadsActive.RemoveAt(delayThreadId);
161   PTRACE(3, "DelayThread " << delayThreadId << " OnRelease all done");
162   --currentSize;
163 }
164 
AppendRunning(PSafePtr<DelayThread> delayThread,PString id)165 void SafeTest::AppendRunning(PSafePtr<DelayThread> delayThread, PString id)
166 {
167   ++currentSize;
168   PTRACE(3, "Add a delay thread of " << id);
169   if (delayThreadsActive.FindWithLock(id) != NULL) {
170     PAssertAlways("Appending multiple instances at the same id");
171   }
172 
173   delayThreadsActive.SetAt(id, delayThread);
174 }
175 
DeleteObject(PObject * object) const176 void SafeTest::DelayThreadsDict::DeleteObject(PObject * object) const
177 {
178   DelayThread * delayThread = PDownCast(DelayThread, object);
179   if (delayThread != NULL) {
180     PTRACE(3, "Delete DelayThread " << *delayThread);
181     delete delayThread;
182   }
183 }
184 
UseOnThreadEnd()185 PBoolean SafeTest::UseOnThreadEnd()
186 {
187   return useOnThreadEnd;
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 
OnDelayThreadEnd(SafeTest & _safeTest,const PString & _delayThreadId)192 OnDelayThreadEnd::OnDelayThreadEnd(SafeTest &_safeTest, const PString & _delayThreadId)
193   : PThread(10000, AutoDeleteThread),
194     safeTest(_safeTest),
195     delayThreadId(_delayThreadId)
196 {
197   Resume();
198 }
199 
Main()200 void OnDelayThreadEnd::Main()
201 {
202   PThread::Sleep(1000);  //Let the DelayThread end good and proper
203 
204   safeTest.OnReleased(delayThreadId);
205 }
206 
207 /////////////////////////////////////////////////////////////////////////////
208 
DelayWorkerThread(DelayThread & _delayThread,PInt64 _iteration)209 DelayWorkerThread::DelayWorkerThread(DelayThread & _delayThread, PInt64 _iteration)
210   : PThread(10000, AutoDeleteThread),
211     delayThread(_delayThread),
212     iteration(_iteration)
213 {
214   thisThreadName << iteration << " Delay Thread";
215   SetThreadName(thisThreadName);
216   Resume();
217 }
218 
Main()219 void DelayWorkerThread::Main()
220 {
221   delayThread.DelayThreadMain(*this, 0000);
222 }
223 
224 /////////////////////////////////////////////////////////////////////////////
225 
DelayThreadTermination(DelayThread & _delayThread)226 DelayThreadTermination::DelayThreadTermination(DelayThread & _delayThread)
227   : PThread(10000, AutoDeleteThread),
228     delayThread(_delayThread)
229 {
230   thisThreadName <<"%X DT term";
231   SetThreadName(thisThreadName);
232   Resume();
233 }
234 
Main()235 void DelayThreadTermination::Main()
236 {
237   delayThread.OnReleaseThreadMain(*this, 0000);
238 }
239 
240 /////////////////////////////////////////////////////////////////////////////
241 
DelayThread(SafeTest & _safeTest,PINDEX _delay,PInt64 iteration)242 DelayThread::DelayThread(SafeTest &_safeTest, PINDEX _delay, PInt64 iteration)
243   : safeTest(_safeTest),
244     delay(_delay)
245 {
246   threadRunning = PTrue;
247 
248   PTRACE(5, "Constructor for a non auto deleted delay thread");
249 
250   if (safeTest.AvoidPThreadCreate()) {
251     new DelayWorkerThread(*this, iteration);
252   } else {
253     name << PString(iteration) << " Delay Thread";
254     PThread::Create(PCREATE_NOTIFIER(DelayThreadMain), 30000,
255 		    PThread::AutoDeleteThread,
256 		    PThread::NormalPriority,
257 		    name);
258   }
259 }
260 
~DelayThread()261 DelayThread::~DelayThread()
262 {
263   if (threadRunning) {
264     PAssertAlways("Destroy this thread while it is still running. Bad karma");
265   }
266 
267   PTRACE(5, "Destructor for a delay thread");
268 }
269 
DelayThreadMain(PThread & thisThread,INT)270 void DelayThread::DelayThreadMain(PThread &thisThread, INT)
271 {
272   id = thisThread.GetThreadName();
273   PTRACE(3, "DelayThread starting " << id);
274   safeTest.AppendRunning(this, id);
275   PThread::Sleep(delay);
276   PTRACE(3, "DelayThread finished " << id);
277 
278 
279   if (safeTest.UseOnThreadEnd()) {
280     threadRunning = PFalse;
281     new OnDelayThreadEnd(safeTest, id);
282   } else {
283     SafeReference();
284     Release();
285   }
286 }
287 
Release()288 void DelayThread::Release()
289 {
290     if (safeTest.AvoidPThreadCreate()) {
291       new DelayThreadTermination(*this);
292     } else {
293       // Add a reference for the thread we are about to start
294       PThread::Create(PCREATE_NOTIFIER(OnReleaseThreadMain), 10000,
295 		      PThread::AutoDeleteThread,
296 		      PThread::NormalPriority,
297 		      "%X: Release");
298     }
299 }
300 
OnReleaseThreadMain(PThread &,INT)301 void DelayThread::OnReleaseThreadMain(PThread &, INT)
302 {
303   safeTest.OnReleased(*this);
304   threadRunning = PFalse;
305   SafeDereference();
306 }
307 
PrintOn(ostream & strm) const308 void DelayThread::PrintOn(ostream & strm) const
309 {
310   strm << id << " ";
311 }
312 ///////////////////////////////////////////////////////////////////////////
313 
314 
ReporterThread(LauncherThread & _launcher)315 ReporterThread::ReporterThread(LauncherThread & _launcher)
316   : PThread(10000, NoAutoDeleteThread),
317     launcher(_launcher)
318 {
319   terminateNow = PFalse;
320   Resume();
321 }
322 
Terminate()323 void ReporterThread::Terminate()
324 {
325   terminateNow = PTrue;
326   exitFlag.Signal();
327 }
328 
Main()329 void ReporterThread::Main()
330 {
331   while (!terminateNow) {
332     exitFlag.Wait(1000 * 60);
333 
334     launcher.ReportAverageTime();
335     launcher.ReportIterations();
336     launcher.ReportElapsedTime();
337     cout << " " << endl << flush;
338   }
339 }
340 
LauncherThread(SafeTest & _safeTest)341 LauncherThread::LauncherThread(SafeTest &_safeTest)
342   : PThread(10000, NoAutoDeleteThread),
343     safeTest(_safeTest)
344 {
345   iteration = 0;
346   keepGoing = PTrue;
347 
348   if (safeTest.RegularReporting())
349     reporter = new ReporterThread(*this);
350   else
351     reporter = NULL;
352 }
353 
~LauncherThread()354 LauncherThread::~LauncherThread()
355 {
356   if (reporter != NULL) {
357     reporter->Terminate();
358     reporter->WaitForTermination();
359     delete reporter;
360     reporter = NULL;
361   }
362 }
363 
364 
Main()365 void LauncherThread::Main()
366 {
367 
368   PINDEX count = safeTest.ActiveCount();
369   while(keepGoing) {
370     PINDEX delay = safeTest.Delay() + safeTest.GetRandom();
371     while(safeTest.CurrentSize() < count) {
372       iteration++;
373       void * location = new DelayThread(safeTest, delay, iteration);
374       if (((PUInt64)location) < 0xffff) {
375 	int a, b;
376 	a = 1;
377 	b = 0;
378 	a = a/b;   //Your handy dandy divide by zero error. Immediate end.
379       }
380     }
381     PThread::Yield();
382   }
383 }
384 
ReportAverageTime()385 void LauncherThread::ReportAverageTime()
386 {
387   PInt64 i = GetIteration();
388   if (i == 0) {
389     cout << "Have not completed an iteration yet, so time per "
390 	 << "iteration is unavailable" << endl;
391   } else {
392     cout << "Average time per iteration is "
393 	 << (GetElapsedTime().GetMilliSeconds()/((double) i))
394 	 << " milliseconds" << endl;
395   }
396 }
397 
ReportIterations()398 void LauncherThread::ReportIterations()
399 {
400   cout << "\nHave completed "
401        << GetIteration() << " iterations" << endl;
402 }
403 
ReportElapsedTime()404 void LauncherThread::ReportElapsedTime()
405 {
406   cout << "\nElapsed time is "
407        << GetElapsedTime()
408        << " (Hours:mins:seconds.millseconds)" << endl;
409 }
410 ////////////////////////////////////////////////////////////////////////////////
411 
Main()412 void UserInterfaceThread::Main()
413 {
414   PConsoleChannel console(PConsoleChannel::StandardInput);
415   cout << "This program will repeatedly create and destroy a thread until "
416        << "terminated from the console" << endl;
417 
418   PStringStream help;
419   help << "Press : " << endl
420        << "         D      average Delay time of each thread" << endl
421        << "         H or ? help"                              << endl
422        << "         R      report count of threads done"      << endl
423        << "         T      time elapsed"                      << endl
424        << "         X or Q exit "                             << endl;
425 
426   cout << endl << help;
427 
428   LauncherThread launch(safeTest);
429   launch.Resume();
430 
431   console.SetReadTimeout(P_MAX_INDEX);
432   for (;;) {
433     int ch = console.ReadChar();
434 
435     switch (tolower(ch)) {
436     case 'd' :
437       {
438         launch.ReportAverageTime();
439         cout << "Command ? " << flush;
440         break;
441       }
442     case 'r' :
443       launch.ReportIterations();
444       cout << "Command ? " << flush;
445       break;
446     case 't' :
447       launch.ReportElapsedTime();
448       cout << "Command ? " << flush;
449       break;
450 
451     case 'x' :
452     case 'q' :
453       cout << "Exiting." << endl;
454       launch.Terminate();
455       launch.WaitForTermination();
456       return;
457       break;
458     case '?' :
459     case 'h' :
460       cout << help << endl;
461       cout << "Command ? " << flush;
462     default:
463       break;
464     } // end switch
465 
466   } // end for
467 
468 }
469 
470 
471 // End of File ///////////////////////////////////////////////////////////////
472