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