xref: /trunk/main/framework/test/threadtest.cxx (revision cdf0e10c)
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