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