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