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