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