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:
CancelJobsThread(std::list<css::uno::Reference<css::util::XCancellable>> aJobs)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
~CancelJobsThread()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
addJobs(std::list<css::uno::Reference<css::util::XCancellable>> & rJobs)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
existJobs() const91 bool CancelJobsThread::existJobs() const
92 {
93 osl::MutexGuard aGuard(maMutex);
94
95 return !maJobs.empty();
96 }
97
allJobsCancelled() const98 bool CancelJobsThread::allJobsCancelled() const
99 {
100 osl::MutexGuard aGuard(maMutex);
101
102 return maJobs.empty() && mbAllJobsCancelled;
103 }
stopWhenAllJobsCancelled()104 void CancelJobsThread::stopWhenAllJobsCancelled()
105 {
106 osl::MutexGuard aGuard(maMutex);
107
108 mbStopped = true;
109 }
110
getNextJob()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
stopped() const128 bool CancelJobsThread::stopped() const
129 {
130 osl::MutexGuard aGuard(maMutex);
131
132 return mbStopped;
133 }
134
run()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:
TerminateOfficeThread(CancelJobsThread & rCancelJobsThread,css::uno::Reference<css::uno::XComponentContext> const & xContext)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
~TerminateOfficeThread()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
StopOfficeTermination()200 void TerminateOfficeThread::StopOfficeTermination()
201 {
202 osl::MutexGuard aGuard(maMutex);
203
204 mbStopOfficeTermination = true;
205 }
206
OfficeTerminationStopped()207 bool TerminateOfficeThread::OfficeTerminationStopped()
208 {
209 osl::MutexGuard aGuard(maMutex);
210
211 return mbStopOfficeTermination;
212 }
213
run()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
PerformOfficeTermination()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
onTerminated()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 */
FinalThreadManager(css::uno::Reference<css::uno::XComponentContext> const & context)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
registerAsListenerAtDesktop()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
~FinalThreadManager()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:
getImplementationName()336 ::rtl::OUString SAL_CALL FinalThreadManager::getImplementationName() throw (css::uno::RuntimeException)
337 {
338 return comp_FinalThreadManager::_getImplementationName();
339 }
340
supportsService(::rtl::OUString const & serviceName)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
getSupportedServiceNames()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:
registerJob(const css::uno::Reference<css::util::XCancellable> & Job)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
releaseJob(const css::uno::Reference<css::util::XCancellable> & Job)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
cancelAllJobs()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
queryTermination(const css::lang::EventObject &)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
cancelTermination(const css::lang::EventObject &)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
notifyTermination(const css::lang::EventObject &)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)
disposing(const css::lang::EventObject &)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
_getImplementationName()524 ::rtl::OUString SAL_CALL _getImplementationName()
525 {
526 return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
527 "com.sun.star.util.comp.FinalThreadManager"));
528 }
529
_getSupportedServiceNames()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
_create(const css::uno::Reference<css::uno::XComponentContext> & context)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