xref: /AOO41X/main/framework/test/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 <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 differnt 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 automaticly 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