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