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