xref: /AOO41X/main/framework/test/threadtest/threadtest.cxx (revision 6d739b60ff8f4ed2134ae1442e284f9da90334b4)
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 differnt 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 automaticly 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