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