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