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 #include "precompiled_sw.hxx" 28 #include <finalthreadmanager.hxx> 29 30 #ifndef _OSL_THREAD_HXX_ 31 #include <osl/thread.hxx> 32 #endif 33 #include <errhdl.hxx> 34 #include <pausethreadstarting.hxx> 35 #include <swthreadjoiner.hxx> 36 37 #include <com/sun/star/frame/XDesktop.hpp> 38 #include <rtl/ustring.hxx> 39 #include <com/sun/star/frame/XFramesSupplier.hpp> 40 41 namespace css = ::com::sun::star; 42 43 /** thread to cancel a give list of cancellable jobs 44 45 helper class for FinalThreadManager 46 47 @author OD 48 */ 49 class CancelJobsThread : public osl::Thread 50 { 51 public: 52 CancelJobsThread( std::list< css::uno::Reference< css::util::XCancellable > > aJobs ) 53 : osl::Thread(), 54 maMutex(), 55 maJobs( aJobs ), 56 mbAllJobsCancelled( false ), 57 mbStopped( false ) 58 { 59 } 60 61 virtual ~CancelJobsThread() {} 62 63 void addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs ); 64 65 bool allJobsCancelled() const; 66 67 void stopWhenAllJobsCancelled(); 68 69 private: 70 71 bool existJobs() const; 72 73 css::uno::Reference< css::util::XCancellable > getNextJob(); 74 75 bool stopped() const; 76 77 virtual void SAL_CALL run(); 78 79 mutable osl::Mutex maMutex; 80 81 std::list< css::uno::Reference< css::util::XCancellable > > maJobs; 82 83 bool mbAllJobsCancelled; 84 bool mbStopped; 85 }; 86 87 void CancelJobsThread::addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs ) 88 { 89 osl::MutexGuard aGuard(maMutex); 90 91 maJobs.insert( maJobs.end(), rJobs.begin(), rJobs.end() ); 92 mbAllJobsCancelled = !maJobs.empty(); 93 } 94 95 bool CancelJobsThread::existJobs() const 96 { 97 osl::MutexGuard aGuard(maMutex); 98 99 return !maJobs.empty(); 100 } 101 102 bool CancelJobsThread::allJobsCancelled() const 103 { 104 osl::MutexGuard aGuard(maMutex); 105 106 return maJobs.empty() && mbAllJobsCancelled; 107 } 108 void CancelJobsThread::stopWhenAllJobsCancelled() 109 { 110 osl::MutexGuard aGuard(maMutex); 111 112 mbStopped = true; 113 } 114 115 css::uno::Reference< css::util::XCancellable > CancelJobsThread::getNextJob() 116 { 117 css::uno::Reference< css::util::XCancellable > xRet; 118 119 { 120 osl::MutexGuard aGuard(maMutex); 121 122 if ( !maJobs.empty() ) 123 { 124 xRet = maJobs.front(); 125 maJobs.pop_front(); 126 } 127 } 128 129 return xRet; 130 } 131 132 bool CancelJobsThread::stopped() const 133 { 134 osl::MutexGuard aGuard(maMutex); 135 136 return mbStopped; 137 } 138 139 void SAL_CALL CancelJobsThread::run() 140 { 141 while ( !stopped() ) 142 { 143 while ( existJobs() ) 144 { 145 css::uno::Reference< css::util::XCancellable > aJob( getNextJob() ); 146 if ( aJob.is() ) 147 { 148 aJob->cancel(); 149 } 150 } 151 152 mbAllJobsCancelled = true; 153 154 { 155 TimeValue aSleepTime; 156 aSleepTime.Seconds = 1; 157 aSleepTime.Nanosec = 0; 158 osl_waitThread( &aSleepTime ); 159 } 160 } 161 } 162 163 /** thread to terminate office, when all jobs are cancelled. 164 165 helper class for FinalThreadManager 166 167 @author OD 168 */ 169 class TerminateOfficeThread : public osl::Thread 170 { 171 public: 172 TerminateOfficeThread( CancelJobsThread& rCancelJobsThread, 173 css::uno::Reference< css::uno::XComponentContext > const & xContext ) 174 : osl::Thread(), 175 maMutex(), 176 mrCancelJobsThread( rCancelJobsThread ), 177 mbStopOfficeTermination( false ), 178 mxContext( xContext ) 179 { 180 } 181 182 virtual ~TerminateOfficeThread() {} 183 184 void StopOfficeTermination(); 185 186 private: 187 188 virtual void SAL_CALL run(); 189 virtual void SAL_CALL onTerminated(); 190 191 bool OfficeTerminationStopped(); 192 193 void PerformOfficeTermination(); 194 195 osl::Mutex maMutex; 196 197 const CancelJobsThread& mrCancelJobsThread; 198 199 bool mbStopOfficeTermination; 200 201 css::uno::Reference< css::uno::XComponentContext > mxContext; 202 }; 203 204 void TerminateOfficeThread::StopOfficeTermination() 205 { 206 osl::MutexGuard aGuard(maMutex); 207 208 mbStopOfficeTermination = true; 209 } 210 211 bool TerminateOfficeThread::OfficeTerminationStopped() 212 { 213 osl::MutexGuard aGuard(maMutex); 214 215 return mbStopOfficeTermination; 216 } 217 218 void SAL_CALL TerminateOfficeThread::run() 219 { 220 while ( !OfficeTerminationStopped() ) 221 { 222 osl::MutexGuard aGuard(maMutex); 223 224 if ( mrCancelJobsThread.allJobsCancelled() ) 225 { 226 break; 227 } 228 } 229 230 if ( !OfficeTerminationStopped() ) 231 { 232 PerformOfficeTermination(); 233 } 234 } 235 236 void TerminateOfficeThread::PerformOfficeTermination() 237 { 238 css::uno::Reference< css::frame::XFramesSupplier > xTasksSupplier( 239 mxContext->getServiceManager()->createInstanceWithContext( 240 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop") ), 241 mxContext ), 242 css::uno::UNO_QUERY ); 243 if ( !xTasksSupplier.is() ) 244 { 245 ASSERT( false, "<TerminateOfficeThread::PerformOfficeTermination()> - no XFramesSupplier!" ); 246 return; 247 } 248 249 css::uno::Reference< css::container::XElementAccess > xList( xTasksSupplier->getFrames(), css::uno::UNO_QUERY ); 250 if ( !xList.is() ) 251 { 252 ASSERT( false, "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" ); 253 return; 254 } 255 256 if ( !xList->hasElements() ) 257 { 258 css::uno::Reference< css::frame::XDesktop > xDesktop( xTasksSupplier, css::uno::UNO_QUERY ); 259 if ( xDesktop.is() && !OfficeTerminationStopped() ) 260 { 261 xDesktop->terminate(); 262 } 263 } 264 } 265 266 void SAL_CALL TerminateOfficeThread::onTerminated() 267 { 268 if ( OfficeTerminationStopped() ) 269 { 270 delete this; 271 } 272 } 273 274 275 /** class FinalThreadManager 276 277 @author OD 278 */ 279 FinalThreadManager::FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const & context) 280 : m_xContext(context), 281 maMutex(), 282 maThreads(), 283 mpCancelJobsThread( 0 ), 284 mpTerminateOfficeThread( 0 ), 285 mpPauseThreadStarting( 0 ), 286 mbRegisteredAtDesktop( false ) 287 { 288 289 } 290 291 void FinalThreadManager::registerAsListenerAtDesktop() 292 { 293 css::uno::Reference< css::frame::XDesktop > xDesktop( 294 m_xContext->getServiceManager()->createInstanceWithContext( 295 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("com.sun.star.frame.Desktop") ), 296 m_xContext ), 297 css::uno::UNO_QUERY ); 298 299 if ( xDesktop.is() ) 300 { 301 xDesktop->addTerminateListener( css::uno::Reference< css::frame::XTerminateListener >( static_cast< cppu::OWeakObject* >( this ), css::uno::UNO_QUERY ) ); 302 } 303 } 304 305 FinalThreadManager::~FinalThreadManager() 306 { 307 if ( mpPauseThreadStarting != 0 ) 308 { 309 delete mpPauseThreadStarting; 310 mpPauseThreadStarting = 0; 311 } 312 313 if ( mpTerminateOfficeThread != 0 ) 314 { 315 mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself. 316 mpTerminateOfficeThread = 0; 317 } 318 319 if ( !maThreads.empty() ) 320 { 321 ASSERT( false, "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" ); 322 cancelAllJobs(); 323 } 324 325 if ( mpCancelJobsThread != 0 ) 326 { 327 if ( !mpCancelJobsThread->allJobsCancelled() ) 328 { 329 ASSERT( false, "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" ); 330 } 331 332 mpCancelJobsThread->stopWhenAllJobsCancelled(); 333 mpCancelJobsThread->join(); 334 delete mpCancelJobsThread; 335 mpCancelJobsThread = 0; 336 } 337 } 338 339 // com.sun.star.uno.XServiceInfo: 340 ::rtl::OUString SAL_CALL FinalThreadManager::getImplementationName() throw (css::uno::RuntimeException) 341 { 342 return comp_FinalThreadManager::_getImplementationName(); 343 } 344 345 ::sal_Bool SAL_CALL FinalThreadManager::supportsService(::rtl::OUString const & serviceName) throw (css::uno::RuntimeException) 346 { 347 css::uno::Sequence< ::rtl::OUString > serviceNames = comp_FinalThreadManager::_getSupportedServiceNames(); 348 for (::sal_Int32 i = 0; i < serviceNames.getLength(); ++i) { 349 if (serviceNames[i] == serviceName) 350 return sal_True; 351 } 352 return sal_False; 353 } 354 355 css::uno::Sequence< ::rtl::OUString > SAL_CALL FinalThreadManager::getSupportedServiceNames() throw (css::uno::RuntimeException) 356 { 357 return comp_FinalThreadManager::_getSupportedServiceNames(); 358 } 359 360 // ::com::sun::star::util::XJobManager: 361 void SAL_CALL FinalThreadManager::registerJob(const css::uno::Reference< css::util::XCancellable > & Job) throw (css::uno::RuntimeException) 362 { 363 osl::MutexGuard aGuard(maMutex); 364 365 maThreads.push_back( Job ); 366 367 if ( !mbRegisteredAtDesktop ) 368 { 369 registerAsListenerAtDesktop(); 370 mbRegisteredAtDesktop = true; 371 } 372 } 373 374 void SAL_CALL FinalThreadManager::releaseJob(const css::uno::Reference< css::util::XCancellable > & Job) throw (css::uno::RuntimeException) 375 { 376 osl::MutexGuard aGuard(maMutex); 377 378 maThreads.remove( Job ); 379 } 380 381 void SAL_CALL FinalThreadManager::cancelAllJobs() throw (css::uno::RuntimeException) 382 { 383 std::list< css::uno::Reference< css::util::XCancellable > > aThreads; 384 { 385 osl::MutexGuard aGuard(maMutex); 386 387 aThreads.insert( aThreads.end(), maThreads.begin(), maThreads.end() ); 388 maThreads.clear(); 389 } 390 391 if ( !aThreads.empty() ) 392 { 393 osl::MutexGuard aGuard(maMutex); 394 395 if ( mpCancelJobsThread == 0 ) 396 { 397 mpCancelJobsThread = new CancelJobsThread( aThreads );; 398 if ( !mpCancelJobsThread->create() ) 399 { 400 // error handling 401 // ASSERT( false, "<FinalThreadManager::cancelAllJobs()> - thread to cancel jobs can't be setup --> synchron cancellation of jobs" ); 402 delete mpCancelJobsThread; 403 mpCancelJobsThread = 0; 404 while ( !aThreads.empty() ) 405 { 406 aThreads.front()->cancel(); 407 aThreads.pop_front(); 408 } 409 } 410 } 411 else 412 { 413 mpCancelJobsThread->addJobs( aThreads ); 414 } 415 } 416 } 417 418 // ::com::sun::star::frame::XTerminateListener 419 void SAL_CALL FinalThreadManager::queryTermination( const css::lang::EventObject& ) throw (css::frame::TerminationVetoException, css::uno::RuntimeException) 420 { 421 osl::MutexGuard aGuard(maMutex); 422 423 cancelAllJobs(); 424 // Sleep 1 second to give the thread for job cancellation some time. 425 // Probably, all started threads have already finished its work. 426 if ( mpCancelJobsThread != 0 && 427 !mpCancelJobsThread->allJobsCancelled() ) 428 { 429 TimeValue aSleepTime; 430 aSleepTime.Seconds = 1; 431 aSleepTime.Nanosec = 0; 432 osl_waitThread( &aSleepTime ); 433 } 434 435 if ( mpCancelJobsThread != 0 && 436 !mpCancelJobsThread->allJobsCancelled() ) 437 { 438 if ( mpTerminateOfficeThread != 0 ) 439 { 440 if ( mpTerminateOfficeThread->isRunning() ) 441 { 442 mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself. 443 } 444 else 445 { 446 delete mpTerminateOfficeThread; 447 } 448 mpTerminateOfficeThread = 0; 449 } 450 mpTerminateOfficeThread = new TerminateOfficeThread( *mpCancelJobsThread, 451 m_xContext ); 452 if ( !mpTerminateOfficeThread->create() ) 453 { 454 // ASSERT( false, "FinalThreadManager::queryTermination(..) - thread to terminate office can't be started!" ); 455 delete mpTerminateOfficeThread; 456 mpTerminateOfficeThread = 0; 457 } 458 459 throw css::frame::TerminationVetoException(); 460 } 461 462 mpPauseThreadStarting = new SwPauseThreadStarting(); 463 464 return; 465 } 466 467 void SAL_CALL FinalThreadManager::cancelTermination( const css::lang::EventObject& ) throw (css::uno::RuntimeException) 468 { 469 if ( mpPauseThreadStarting != 0 ) 470 { 471 delete mpPauseThreadStarting; 472 mpPauseThreadStarting = 0; 473 } 474 475 return; 476 } 477 478 void SAL_CALL FinalThreadManager::notifyTermination( const css::lang::EventObject& ) throw (css::uno::RuntimeException) 479 { 480 if ( mpTerminateOfficeThread != 0 ) 481 { 482 if ( mpTerminateOfficeThread->isRunning() ) 483 { 484 // ASSERT( false, "<FinalThreadManager::notifyTermination()> - office termination thread still running!" ); 485 mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself. 486 } 487 else 488 { 489 delete mpTerminateOfficeThread; 490 } 491 mpTerminateOfficeThread = 0; 492 } 493 494 if ( !maThreads.empty() ) 495 { 496 // ASSERT( false, "<FinalThreadManager::notifyTermination()> - still registered jobs are existing" ); 497 cancelAllJobs(); 498 } 499 500 if ( mpCancelJobsThread != 0 ) 501 { 502 if ( !mpCancelJobsThread->allJobsCancelled() ) 503 { 504 // ASSERT( false, "<FinalThreadManager::notifyTermination()> - cancellation of registered jobs not yet finished -> wait for its finish" ); 505 } 506 507 mpCancelJobsThread->stopWhenAllJobsCancelled(); 508 mpCancelJobsThread->join(); 509 delete mpCancelJobsThread; 510 mpCancelJobsThread = 0; 511 } 512 513 // get reference of this 514 css::uno::Reference< css::uno::XInterface > aOwnRef( static_cast< cppu::OWeakObject* >( this )); 515 // notify <SwThreadJoiner> to release its reference 516 SwThreadJoiner::ReleaseThreadJoiner(); 517 } 518 519 // ::com::sun:star::lang::XEventListener (inherited via com::sun::star::frame::XTerminateListener) 520 void SAL_CALL FinalThreadManager::disposing( const css::lang::EventObject& ) throw (css::uno::RuntimeException) 521 { 522 // nothing to do, because instance doesn't hold any references of observed objects 523 } 524 525 // component helper namespace 526 namespace comp_FinalThreadManager { 527 528 ::rtl::OUString SAL_CALL _getImplementationName() 529 { 530 return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 531 "com.sun.star.util.comp.FinalThreadManager")); 532 } 533 534 css::uno::Sequence< ::rtl::OUString > SAL_CALL _getSupportedServiceNames() 535 { 536 css::uno::Sequence< ::rtl::OUString > s(1); 537 s[0] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( 538 "com.sun.star.util.JobManager")); 539 return s; 540 } 541 542 css::uno::Reference< css::uno::XInterface > SAL_CALL _create( 543 const css::uno::Reference< css::uno::XComponentContext > & context) 544 SAL_THROW((css::uno::Exception)) 545 { 546 return static_cast< ::cppu::OWeakObject * >(new FinalThreadManager(context)); 547 } 548 549 } // closing component helper namespace 550