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