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