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