1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_framework.hxx"
26
27 //_________________________________________________________________________________________________________________
28 // my own includes
29 //_________________________________________________________________________________________________________________
30 #include <macros/generic.hxx>
31 #include <macros/debug.hxx>
32 #include <threadhelp/resetableguard.hxx>
33 #include <threadhelp/transactionguard.hxx>
34
35 #ifndef __FRAMEWORK_THREADHELP_RWLOCKBASE_HXX_
36 #include <threadhelp/rwlockbase.hxx>
37 #endif
38
39 #ifndef __FRAMEWORK_THREADHELP_TRANSACTIONBASE_HXX_
40 #include <threadhelp/transactionbase.hxx>
41 #endif
42 #include <threadhelp/readguard.hxx>
43 #include <threadhelp/writeguard.hxx>
44
45 //_________________________________________________________________________________________________________________
46 // interface includes
47 //_________________________________________________________________________________________________________________
48
49 //_________________________________________________________________________________________________________________
50 // other includes
51 //_________________________________________________________________________________________________________________
52 #include <rtl/random.h>
53 #include <vos/process.hxx>
54 #include <vos/thread.hxx>
55 #include <rtl/ustring.hxx>
56 #include <rtl/ustrbuf.hxx>
57 #include <osl/time.h>
58
59 #ifndef _OSL_INTERLOCK_H_
60 #include <osl/interlock.h>
61 #endif
62
63 #include <vcl/event.hxx>
64 #include <vcl/svapp.hxx>
65 #include <vcl/wrkwin.hxx>
66 #include <vcl/msgbox.hxx>
67 #include <stdio.h>
68
69 //_________________________________________________________________________________________________________________
70 // const
71 //_________________________________________________________________________________________________________________
72
73 #define LOGFILE "threadtest.log"
74 #define STATISTICS_FILE "threadtest_statistic.csv"
75
76 //_________________________________________________________________________________________________________________
77 // namespace
78 //_________________________________________________________________________________________________________________
79
80 using namespace ::rtl ;
81 using namespace ::osl ;
82 using namespace ::vos ;
83 using namespace ::framework ;
84
85 //_________________________________________________________________________________________________________________
86 // defines
87 //_________________________________________________________________________________________________________________
88
89 /*---------------- Use follow defines to enable/disable some special features of this little test program! -------*/
90
91 #define ENABLE_LOG
92 //#define ENABLE_THREADDELAY
93 #define ENABLE_REQUESTCOUNT
94
95 /*----------------------------------------------------------------------------------------------------------------*/
96
97 #ifdef ENABLE_LOG
98 #define LOG_SETA_START( NA, NID ) \
99 { \
100 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
101 ResetableGuard aLogGuard( m_aLogMutex ); \
102 OStringBuffer sLog(256); \
103 sLog.append( (sal_Int32)nTimeStamp ); \
104 sLog.append( ": Thread[ " ); \
105 sLog.append( NID ); \
106 sLog.append( " ] call setA( " ); \
107 sLog.append( NA ); \
108 sLog.append( " )\n" ); \
109 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
110 }
111
112 #define LOG_SETA_END( NA, EREASON, NID ) \
113 { \
114 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
115 ResetableGuard aLogGuard( m_aLogMutex ); \
116 OStringBuffer sLog(256); \
117 sLog.append( (sal_Int32)nTimeStamp ); \
118 sLog.append( ": Thread[ " ); \
119 sLog.append( NID ); \
120 if( EREASON == E_NOREASON ) \
121 sLog.append( " ] finish setA( " ); \
122 else \
123 sLog.append( " ] was refused at setA( "); \
124 sLog.append( NA ); \
125 sLog.append( " )\n" ); \
126 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
127 }
128
129 #define LOG_GETA_START( NID ) \
130 { \
131 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
132 ResetableGuard aLogGuard( m_aLogMutex ); \
133 OStringBuffer sLog(256); \
134 sLog.append( (sal_Int32)nTimeStamp ); \
135 sLog.append( ": Thread[ " ); \
136 sLog.append( NID ); \
137 sLog.append( " ] call getA()\n" ); \
138 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
139 }
140
141 #define LOG_GETA_END( NRETURN, EREASON, NID ) \
142 { \
143 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
144 ResetableGuard aLogGuard( m_aLogMutex ); \
145 OStringBuffer sLog(256); \
146 sLog.append( (sal_Int32)nTimeStamp ); \
147 sLog.append( ": Thread[ " ); \
148 sLog.append( NID ); \
149 if( EREASON == E_NOREASON ) \
150 sLog.append( " ] finish getA() with " ); \
151 else \
152 sLog.append( " ] was refused at getA() with " ); \
153 sLog.append( NRETURN ); \
154 sLog.append( "\n" ); \
155 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
156 }
157
158 #define LOG_WORKA_START( NA, NID ) \
159 { \
160 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
161 ResetableGuard aLogGuard( m_aLogMutex ); \
162 OStringBuffer sLog(256); \
163 sLog.append( (sal_Int32)nTimeStamp ); \
164 sLog.append( ": Thread[ " ); \
165 sLog.append( NID ); \
166 sLog.append( " ] call workA( " ); \
167 sLog.append( NA ); \
168 sLog.append( " )\n" ); \
169 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
170 }
171
172 #define LOG_WORKA_END( NRETURN, EREASON, NID ) \
173 { \
174 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
175 ResetableGuard aLogGuard( m_aLogMutex ); \
176 OStringBuffer sLog(256); \
177 sLog.append( (sal_Int32)nTimeStamp ); \
178 sLog.append( ": Thread[ " ); \
179 sLog.append( NID ); \
180 if( EREASON == E_NOREASON ) \
181 sLog.append( " ] finish workA() with " ); \
182 else \
183 sLog.append( " ] was refused at workA() with " ); \
184 sLog.append( NRETURN ); \
185 sLog.append( "\n" ); \
186 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
187 }
188
189 #define LOG_INITEXCEPTION( SMETHOD, NID ) \
190 { \
191 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
192 ResetableGuard aLogGuard( m_aLogMutex ); \
193 OStringBuffer sLog(256); \
194 sLog.append( (sal_Int32)nTimeStamp ); \
195 sLog.append( ": Thread[ " ); \
196 sLog.append( NID ); \
197 sLog.append( " ] get EInitException from \"" ); \
198 sLog.append( SMETHOD ); \
199 sLog.append( "\"\n" ); \
200 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
201 }
202
203 #define LOG_CLOSEEXCEPTION( SMETHOD, NID ) \
204 { \
205 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
206 ResetableGuard aLogGuard( m_aLogMutex ); \
207 OStringBuffer sLog(256); \
208 sLog.append( (sal_Int32)nTimeStamp ); \
209 sLog.append( ": Thread[ " ); \
210 sLog.append( NID ); \
211 sLog.append( " ] get ECloseException from \"" ); \
212 sLog.append( SMETHOD ); \
213 sLog.append( "\"\n" ); \
214 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
215 }
216
217 #define LOG_INIT( NA, NID ) \
218 { \
219 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
220 ResetableGuard aLogGuard( m_aLogMutex ); \
221 OStringBuffer sLog(256); \
222 sLog.append( (sal_Int32)nTimeStamp ); \
223 sLog.append( ": Thread[ " ); \
224 sLog.append( NID ); \
225 sLog.append( " ] initialize me with " ); \
226 sLog.append( NA ); \
227 sLog.append( "\n" ); \
228 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
229 }
230
231 #define LOG_CLOSE( NID ) \
232 { \
233 sal_uInt32 nTimeStamp = osl_getGlobalTimer(); \
234 ResetableGuard aLogGuard( m_aLogMutex ); \
235 OStringBuffer sLog(256); \
236 sLog.append( (sal_Int32)nTimeStamp ); \
237 sLog.append( ": Thread[ " ); \
238 sLog.append( NID ); \
239 sLog.append( " ] close me\n" ); \
240 WRITE_LOGFILE( LOGFILE, sLog.makeStringAndClear() ) \
241 }
242 #else
243 #define LOG_SETA_START( NA, NID )
244 #define LOG_SETA_END( NA, EREASON, NID )
245 #define LOG_GETA_START( NID )
246 #define LOG_GETA_END( NRETURN, EREASON, NID )
247 #define LOG_WORKA_START( NA, NID )
248 #define LOG_WORKA_END( NRETURN, EREASON, NID )
249 #define LOG_INITEXCEPTION( SMETHOD, NID )
250 #define LOG_CLOSEEXCEPTION( SMETHOD, NID )
251 #define LOG_INIT( NA, NID )
252 #define LOG_CLOSE( NID )
253 #endif
254
255 //_________________________________________________________________________________________________________________
256 // declarations
257 //_________________________________________________________________________________________________________________
258
getRandomValue()259 sal_uInt16 getRandomValue()
260 {
261 // Get new random value for thread-sleep!
262 // See run() for further informations.
263 // Always calculate a new random number.
264 sal_uInt16 nValue;
265 rtlRandomPool aPool = rtl_random_createPool();
266 rtl_random_getBytes ( aPool, &nValue, 2 );
267 rtl_random_destroyPool ( aPool );
268 return nValue;
269 }
270
271 /*-************************************************************************************************************//**
272 @descr This class is used from different threads at the same time.
273 We start working after calling init() first(!) ...
274 and finish it by calling close(). It exist two methods for reading/writing an
275 internal variable "A". Another function workA() do both things at the same time.
276 All public methods log information in a file if DO_LOG is defined.
277
278 @attention Our public base class FaiRWLockBase is a struct with a RWLock as member.
279 This member can be used by guards to safe access at internal variables
280 in interface methods.
281 Another baseclass is the TransactionBase. They support rejection of wrong calls at wrong time.
282 e.g. calls after closing object!
283 *//*-*************************************************************************************************************/
284
285 class ThreadSafeClass : private TransactionBase
286 , private FairRWLockBase
287 {
288 public:
289
290 ThreadSafeClass ();
291 ~ThreadSafeClass();
292
293 // This methods are used from different threads
294 // to test this class.
295 void init ( sal_Int32 nA ,
296 sal_Int32 nThreadID );
297 void close ( sal_Int32 nThreadID );
298 void setA ( sal_Int32 nA ,
299 sal_Int32 nThreadID );
300 sal_Int32 getA ( sal_Int32 nThreadID );
301 sal_Int32 workA ( sal_Int32 nA ,
302 sal_Int32 nThreadID );
303
304 #ifdef ENABLE_REQUESTCOUNT
305 // This methods are used for statistics only!
getReadCount()306 sal_Int32 getReadCount () { return m_nReadCount; }
getWriteCount()307 sal_Int32 getWriteCount() { return m_nWriteCount; }
308 #endif
309
310 private:
311
312 sal_Int32 m_nA ; /// test member fro reading/writing
313
314 #ifdef ENABLE_LOG
315 ::osl::Mutex m_aLogMutex ; /// mutex to serialize writing log file!
316 #endif
317
318 #ifdef ENABLE_REQUESTCOUNT
319 oslInterlockedCount m_nReadCount ; /// statistic variables to count read/write requests
320 oslInterlockedCount m_nWriteCount ;
321 #endif
322 };
323
324 //_________________________________________________________________________________________________________________
ThreadSafeClass()325 ThreadSafeClass::ThreadSafeClass()
326 : TransactionBase ( )
327 , FairRWLockBase ( )
328 , m_nA ( 0 )
329 #ifdef ENABLE_REQUESTCOUNT
330 , m_nReadCount ( 0 )
331 , m_nWriteCount ( 0 )
332 #endif
333 {
334 }
335
336 //_________________________________________________________________________________________________________________
~ThreadSafeClass()337 ThreadSafeClass::~ThreadSafeClass()
338 {
339 }
340
341 //_________________________________________________________________________________________________________________
init(sal_Int32 nA,sal_Int32 nThreadID)342 void ThreadSafeClass::init( sal_Int32 nA, sal_Int32 nThreadID )
343 {
344 // Set write lock for setting internal member AND
345 // protect changing of working mode!
346 WriteGuard aWriteLock( m_aLock );
347
348 LOG_INIT( nA, nThreadID )
349
350 // Look for multiple calls of this method first!
351 // Use E_SOFTEXCEPTIONS to disable automatically throwing of exceptions for some working modes.
352 ERejectReason eReason;
353 TransactionGuard aTransaction( m_aTransactionManager, E_NOEXCEPTIONS, eReason );
354 if( eReason == E_UNINITIALIZED )
355 {
356 // OK, it must be the first call and we are synchronized with all other threads by using the write lock!
357 // Otherwise (e.g. if working mode == E_WORK) we get a exception and follow lines are never called.
358
359 // We can set our member and change the working mode now.
360 m_nA = nA;
361 m_aTransactionManager.setWorkingMode( E_WORK );
362 }
363 }
364
365 //_________________________________________________________________________________________________________________
close(sal_Int32 nThreadID)366 void ThreadSafeClass::close( sal_Int32 nThreadID )
367 {
368 // Make it threadsafe.
369 // It must be an exclusiv access! => WriteLock!
370 WriteGuard aWriteLock( m_aLock );
371
372 LOG_CLOSE( nThreadID )
373
374 // We must look for multiple calls of this method.
375 // Try to register this method as a transaction.
376 // In combination with E_HARDEXCEPTIONS only working mode E_WORK pass this barrier.
377 ERejectReason eReason;
378 TransactionGuard aTransaction( m_aTransactionManager, E_NOEXCEPTIONS, eReason );
379 if( eReason == E_NOREASON )
380 {
381 // Change working mode to BEFORECLOSE to enable rejection of normal interface calls
382 // and enable SOFTEXCEPTION mode for some impl- or helper methods!
383 // Attention: We must stop successful registered transaction first ...
384 // because setWorkingMode() blocks and wait for all current existing ones!
385 aTransaction.stop();
386 m_aTransactionManager.setWorkingMode( E_BEFORECLOSE );
387
388 // Now we are alone ...
389 // All further calls to this object are rejected ...
390 // (not all ... some special ones can work by using E_SOFTEXCEPTIONS!)
391
392 // Deinitialize all member and set working mode to E_CLOSE.
393 m_nA = 0;
394 m_aTransactionManager.setWorkingMode( E_CLOSE );
395 }
396 }
397
398 //_________________________________________________________________________________________________________________
setA(sal_Int32 nA,sal_Int32 nThreadID)399 void ThreadSafeClass::setA( sal_Int32 nA, sal_Int32 nThreadID )
400 {
401 // Make it threadsafe.
402 WriteGuard aWriteLock( m_aLock );
403
404 LOG_SETA_START( nA, nThreadID )
405
406 // Register this method as a transaction to prevent code against wrong calls
407 // after close() or before init()!
408 ERejectReason eReason;
409 TransactionGuard aTransaction( m_aTransactionManager, E_NOEXCEPTIONS, eReason );
410 if( eReason == E_NOREASON )
411 {
412 // This object is ready for working and we have full write access.
413 // We can work with our member.
414 m_nA = nA;
415 #ifdef ENABLE_REQUESTCOUNT
416 osl_incrementInterlockedCount( &m_nWriteCount );
417 #endif
418 }
419 LOG_SETA_END( nA, eReason, nThreadID )
420 }
421
422 //_________________________________________________________________________________________________________________
getA(sal_Int32 nThreadID)423 sal_Int32 ThreadSafeClass::getA( sal_Int32 nThreadID )
424 {
425 // Make it threadsafe.
426 ReadGuard aReadLock( m_aLock );
427
428 LOG_GETA_START( nThreadID )
429
430 // Register this method as a transaction to prevent code against wrong calls
431 // after close() or before init()!
432 sal_Int32 nReturn = 0;
433 ERejectReason eReason;
434 TransactionGuard aTransaction( m_aTransactionManager, E_NOEXCEPTIONS, eReason );
435 if( eReason == E_NOREASON )
436 {
437 // This object is ready for working and we have a read access.
438 // We can work with our member.
439 nReturn = m_nA;
440 #ifdef ENABLE_REQUESTCOUNT
441 osl_incrementInterlockedCount( &m_nReadCount );
442 #endif
443 }
444
445 LOG_GETA_END( nReturn, eReason, nThreadID )
446 return nReturn;
447 }
448
449 //_________________________________________________________________________________________________________________
workA(sal_Int32 nA,sal_Int32 nThreadID)450 sal_Int32 ThreadSafeClass::workA( sal_Int32 nA ,
451 sal_Int32 nThreadID )
452 {
453 // This method test the downgrade-mechanism of used lock implementation!
454 // Make it threadsafe.
455 WriteGuard aWriteLock( m_aLock );
456
457 LOG_WORKA_START( nA, nThreadID )
458
459 // Register this method as a transaction to prevent code against wrong calls
460 // after close() or before init()!
461 sal_Int32 nReturn = 0;
462 ERejectReason eReason;
463 TransactionGuard aTransaction( m_aTransactionManager, E_NOEXCEPTIONS, eReason );
464 if( eReason == E_NOREASON )
465 {
466 // We have write access to our member.
467 // Set new value.
468 m_nA = nA;
469 #ifdef ENABLE_REQUESTCOUNT
470 osl_incrementInterlockedCount( &m_nWriteCount );
471 #endif
472
473 // Downgrade write access to read access and read the set value again.
474 // This call can't be rejected - but it can fail!
475 aWriteLock.downgrade();
476 nReturn = m_nA;
477 #ifdef ENABLE_REQUESTCOUNT
478 osl_incrementInterlockedCount( &m_nReadCount );
479 #endif
480 }
481
482 LOG_WORKA_END( nReturn, eReason, nThreadID )
483 return nReturn;
484 }
485
486 /*-****************************************************************************************************//**
487 @descr Every thread instance of these class lopp from 0 up to "nLoops".
488 He sleep for a random time and work with given test class "pClass" then.
489 We use random values for waiting for better results!
490 Otherwise all threads are sychron after first 2,3...5 calls - I think!
491 *//*-*****************************************************************************************************/
492
493 class TestThread : public OThread
494 {
495 public:
496
497 TestThread( ThreadSafeClass* pClass ,
498 sal_Int32 nLoops ,
499 Condition* pListener ,
500 sal_Bool bOwner = sal_False );
501
502 private:
503
504 virtual void SAL_CALL run ();
505 virtual void SAL_CALL onTerminated ();
506
507 private:
508
509 ThreadSafeClass* m_pClass ;
510 sal_Int32 m_nLoops ;
511 sal_Int32 m_nThreadID ;
512 Condition* m_pListener ;
513 sal_Bool m_bOwner ;
514 };
515
516 //_________________________________________________________________________________________________________________
TestThread(ThreadSafeClass * pClass,sal_Int32 nLoops,Condition * pListener,sal_Bool bOwner)517 TestThread::TestThread( ThreadSafeClass* pClass ,
518 sal_Int32 nLoops ,
519 Condition* pListener ,
520 sal_Bool bOwner )
521 : m_pClass ( pClass )
522 , m_nLoops ( nLoops )
523 , m_pListener ( pListener )
524 , m_bOwner ( bOwner )
525 {
526 }
527
528 //_________________________________________________________________________________________________________________
run()529 void SAL_CALL TestThread::run()
530 {
531 // Get ID of this thread.
532 // Is used for logging information ...
533 m_nThreadID = getCurrentIdentifier();
534
535 // If we are the owner of given pClass
536 // we must initialize ... and close
537 // it. See at the end of this method too.
538 if( m_bOwner == sal_True )
539 {
540 m_pClass->init( 0, m_nThreadID );
541 }
542
543 #ifdef ENABLE_THREADDELAY
544 TimeValue nDelay ;
545 #endif
546
547 sal_Int32 nA ;
548
549 for( sal_Int32 nCount=0; nCount<m_nLoops; ++nCount )
550 {
551 // Work with class.
552 // Use random to select called method.
553 nA = (sal_Int32)getRandomValue();
554 if( nA % 5 == 0 )
555 {
556 nA = m_pClass->workA( nA, m_nThreadID );
557 }
558 else
559 if( nA % 3 == 0 )
560 {
561 m_pClass->setA( nA, m_nThreadID );
562 }
563 else
564 {
565 nA = m_pClass->getA( m_nThreadID );
566 }
567 #ifdef ENABLE_THREADDELAY
568 // Sleep - use random value to do that too!
569 nDelay.Seconds = 0;
570 nDelay.Nanosec = getRandomValue();
571 sleep( nDelay );
572 #endif
573 }
574
575 // Don't forget to "close" teset object if you are the owner!
576 if( m_bOwner == sal_True )
577 {
578 m_pClass->close( m_nThreadID );
579 }
580 }
581
582 //_________________________________________________________________________________________________________________
onTerminated()583 void SAL_CALL TestThread::onTerminated()
584 {
585 // Destroy yourself if you finished.
586 // But don't forget to call listener before.
587 m_pListener->set();
588
589 m_pClass = NULL;
590 m_pListener = NULL;
591
592 delete this;
593 }
594
595 /*-****************************************************************************************************//**
596 @descr This is our test application.
597 We create one ThreadSafeClass object and a lot of threads
598 which use it at different times.
599 *//*-*****************************************************************************************************/
600
601 struct ThreadInfo
602 {
603 Condition* pCondition ;
604 TestThread* pThread ;
605 };
606
607 class TestApplication : public Application
608 {
609 public:
610 void Main ( );
611 sal_Int32 measureTime ( sal_Int32 nThreadCount ,
612 sal_Int32 nOwner ,
613 sal_Int32 nLoops=0 );
614 };
615
616 //_________________________________________________________________________________________________________________
617 // definition
618 //_________________________________________________________________________________________________________________
619
620 TestApplication aApplication;
621
622 //_________________________________________________________________________________________________________________
623 // This function start "nThreadCount" threads to use same test class.
624 // You can specify the owner thread of this test class which start/stop it by using "nOwner". [1..nThreadcount]!
625 // If you specify "nLoops" different from 0 we use it as loop count for every started thread.
626 // Otherwise we work with random values.
measureTime(sal_Int32 nThreadCount,sal_Int32 nOwner,sal_Int32 nLoops)627 sal_Int32 TestApplication::measureTime( sal_Int32 nThreadCount ,
628 sal_Int32 nOwner ,
629 sal_Int32 nLoops )
630 {
631 // This is the class which should be tested.
632 ThreadSafeClass aClass;
633
634 // Create list of threads.
635 ThreadInfo* pThreads = new ThreadInfo[nThreadCount];
636 sal_Int32 nLoopCount = nLoops ;
637 sal_Bool bOwner = sal_False ;
638 for( sal_Int32 nI=1; nI<=nThreadCount; ++nI )
639 {
640 // If nLoops==0 => we must use random value; otherwise we must use given count ...
641 if( nLoops == 0 )
642 {
643 nLoopCount = getRandomValue();
644 }
645 // Search owner of class.
646 bOwner = sal_False;
647 if( nOwner == nI )
648 {
649 bOwner = sal_True;
650 }
651 // initialize condition.
652 pThreads[nI].pCondition = new Condition;
653 // Initialize thread.
654 pThreads[nI].pThread = new TestThread( &aClass, nLoopCount, pThreads[nI].pCondition, bOwner );
655 }
656
657 // Start clock to get information about used time.
658 sal_uInt32 nStartTime ;
659 sal_uInt32 nEndTime ;
660
661 nStartTime = osl_getGlobalTimer();
662
663 // Start threads ...
664 for( nI=1; nI<=nThreadCount; ++nI )
665 {
666 pThreads[nI].pThread->create();
667 }
668
669 // Wait for threads ...
670 for( nI=1; nI<=nThreadCount; ++nI )
671 {
672 pThreads[nI].pCondition->wait();
673 delete pThreads[nI].pCondition;
674 pThreads[nI].pCondition = NULL;
675 }
676
677 delete[] pThreads;
678 pThreads = NULL;
679
680 nEndTime = osl_getGlobalTimer();
681
682 // Calc used time and return it. [ms]
683 return( nEndTime-nStartTime );
684 }
685
686 //_________________________________________________________________________________________________________________
Main()687 void TestApplication::Main()
688 {
689 sal_Int32 nTestCount = 0; /// count of calling "measureTime()"
690 sal_Int32 nThreadCount = 0; /// count of used threads by "measure..."
691 sal_Int32 nLoops = 0; /// loop count for every thread
692 sal_Int32 nOwner = 0; /// number of owner thread
693
694 // Parse command line.
695 // Attention: All parameter are required and must exist!
696 // syntax: "threadtest.exe <testcount> <threadcount> <loops> <owner>"
697 OStartupInfo aInfo ;
698 OUString sArgument ;
699 sal_Int32 nArgument ;
700 sal_Int32 nCount = aInfo.getCommandArgCount();
701
702 LOG_ASSERT2( nCount!=4 ,"TestApplication::Main()" , "Wrong argument line detected!")
703
704 for( nArgument=0; nArgument<nCount; ++nArgument )
705 {
706 aInfo.getCommandArg( nArgument, sArgument );
707 if( nArgument== 0 ) nTestCount =sArgument.toInt32();
708 if( nArgument== 1 ) nThreadCount=sArgument.toInt32();
709 if( nArgument== 2 ) nLoops =sArgument.toInt32();
710 if( nArgument== 3 ) nOwner =sArgument.toInt32();
711 }
712
713 // Start test.
714 OStringBuffer sBuf(256);
715 sal_Int32 nTime=0;
716 sBuf.append( "Nr.\tTime\tThreadCount\tLoops\tOwner\n" );
717 for( sal_Int32 nI=1; nI<=nTestCount; ++nI )
718 {
719 nTime = measureTime( nThreadCount, nOwner, nLoops );
720 sBuf.append( nI );
721 sBuf.append( "\t" );
722 sBuf.append( nTime );
723 sBuf.append( "\t" );
724 sBuf.append( nThreadCount );
725 sBuf.append( "\t" );
726 sBuf.append( nLoops );
727 sBuf.append( "\t" );
728 sBuf.append( nOwner );
729 sBuf.append( "\n" );
730 }
731
732 WRITE_LOGFILE( STATISTICS_FILE, sBuf.makeStringAndClear() );
733 LOG_ERROR( "TApplication::Main()", "Test finish successful!" )
734 }
735