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