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 :
20 : #include <finalthreadmanager.hxx>
21 :
22 : #include <osl/thread.hxx>
23 : #include <pausethreadstarting.hxx>
24 : #include <swthreadjoiner.hxx>
25 :
26 : #include <com/sun/star/frame/Desktop.hpp>
27 : #include <rtl/ustring.hxx>
28 : #include <com/sun/star/frame/XFramesSupplier.hpp>
29 : #include <cppuhelper/supportsservice.hxx>
30 :
31 : /** thread to cancel a give list of cancellable jobs
32 :
33 : helper class for FinalThreadManager
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() SAL_OVERRIDE;
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 :
90 0 : void CancelJobsThread::stopWhenAllJobsCancelled()
91 : {
92 0 : osl::MutexGuard aGuard(maMutex);
93 :
94 0 : mbStopped = true;
95 0 : }
96 :
97 0 : css::uno::Reference< css::util::XCancellable > CancelJobsThread::getNextJob()
98 : {
99 0 : css::uno::Reference< css::util::XCancellable > xRet;
100 :
101 : {
102 0 : osl::MutexGuard aGuard(maMutex);
103 :
104 0 : if ( !maJobs.empty() )
105 : {
106 0 : xRet = maJobs.front();
107 0 : maJobs.pop_front();
108 0 : }
109 : }
110 :
111 0 : return xRet;
112 : }
113 :
114 0 : bool CancelJobsThread::stopped() const
115 : {
116 0 : osl::MutexGuard aGuard(maMutex);
117 :
118 0 : return mbStopped;
119 : }
120 :
121 0 : void SAL_CALL CancelJobsThread::run()
122 : {
123 0 : osl_setThreadName("sw CancelJobsThread");
124 :
125 0 : while ( !stopped() )
126 : {
127 0 : while ( existJobs() )
128 : {
129 0 : css::uno::Reference< css::util::XCancellable > aJob( getNextJob() );
130 0 : if ( aJob.is() )
131 0 : aJob->cancel();
132 0 : }
133 :
134 0 : mbAllJobsCancelled = true;
135 :
136 : {
137 : TimeValue aSleepTime;
138 0 : aSleepTime.Seconds = 1;
139 0 : aSleepTime.Nanosec = 0;
140 0 : osl_waitThread( &aSleepTime );
141 : }
142 : }
143 0 : }
144 :
145 : /** thread to terminate office, when all jobs are cancelled.
146 :
147 : helper class for FinalThreadManager
148 : */
149 : class TerminateOfficeThread : public osl::Thread
150 : {
151 : public:
152 0 : TerminateOfficeThread( CancelJobsThread& rCancelJobsThread,
153 : css::uno::Reference< css::uno::XComponentContext > const & xContext )
154 : : osl::Thread(),
155 : maMutex(),
156 : mrCancelJobsThread( rCancelJobsThread ),
157 : mbStopOfficeTermination( false ),
158 0 : mxContext( xContext )
159 : {
160 0 : }
161 :
162 0 : virtual ~TerminateOfficeThread() {}
163 : void StopOfficeTermination();
164 :
165 : private:
166 : virtual void SAL_CALL run() SAL_OVERRIDE;
167 : virtual void SAL_CALL onTerminated() SAL_OVERRIDE;
168 : bool OfficeTerminationStopped();
169 : void PerformOfficeTermination();
170 :
171 : osl::Mutex maMutex;
172 :
173 : const CancelJobsThread& mrCancelJobsThread;
174 : bool mbStopOfficeTermination;
175 :
176 : css::uno::Reference< css::uno::XComponentContext > mxContext;
177 : };
178 :
179 0 : void TerminateOfficeThread::StopOfficeTermination()
180 : {
181 0 : osl::MutexGuard aGuard(maMutex);
182 :
183 0 : mbStopOfficeTermination = true;
184 0 : }
185 :
186 0 : bool TerminateOfficeThread::OfficeTerminationStopped()
187 : {
188 0 : osl::MutexGuard aGuard(maMutex);
189 :
190 0 : return mbStopOfficeTermination;
191 : }
192 :
193 0 : void SAL_CALL TerminateOfficeThread::run()
194 : {
195 0 : osl_setThreadName("sw TerminateOfficeThread");
196 :
197 0 : while ( !OfficeTerminationStopped() )
198 : {
199 0 : osl::MutexGuard aGuard(maMutex);
200 :
201 0 : if ( mrCancelJobsThread.allJobsCancelled() )
202 0 : break;
203 0 : }
204 :
205 0 : if ( !OfficeTerminationStopped() )
206 0 : PerformOfficeTermination();
207 0 : }
208 :
209 0 : void TerminateOfficeThread::PerformOfficeTermination()
210 : {
211 0 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(mxContext);
212 :
213 0 : css::uno::Reference< css::container::XElementAccess > xList( xDesktop->getFrames(), css::uno::UNO_QUERY );
214 0 : if ( !xList.is() )
215 : {
216 : OSL_FAIL( "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" );
217 0 : return;
218 : }
219 :
220 0 : if ( !xList->hasElements() )
221 : {
222 0 : if ( !OfficeTerminationStopped() )
223 0 : xDesktop->terminate();
224 0 : }
225 : }
226 :
227 0 : void SAL_CALL TerminateOfficeThread::onTerminated()
228 : {
229 0 : if ( OfficeTerminationStopped() )
230 0 : delete this;
231 0 : }
232 :
233 4 : FinalThreadManager::FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const & context)
234 : : m_xContext(context),
235 : maMutex(),
236 : maThreads(),
237 : mpCancelJobsThread( 0 ),
238 : mpTerminateOfficeThread( 0 ),
239 : mpPauseThreadStarting( 0 ),
240 4 : mbRegisteredAtDesktop( false )
241 : {
242 :
243 4 : }
244 :
245 2 : void FinalThreadManager::registerAsListenerAtDesktop()
246 : {
247 2 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
248 2 : xDesktop->addTerminateListener( css::uno::Reference< css::frame::XTerminateListener >( static_cast< cppu::OWeakObject* >( this ), css::uno::UNO_QUERY ) );
249 2 : }
250 :
251 12 : FinalThreadManager::~FinalThreadManager()
252 : {
253 4 : if ( mpPauseThreadStarting != 0 )
254 : {
255 2 : delete mpPauseThreadStarting;
256 2 : mpPauseThreadStarting = 0;
257 : }
258 :
259 4 : if ( mpTerminateOfficeThread != 0 )
260 : {
261 0 : mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
262 0 : mpTerminateOfficeThread = 0;
263 : }
264 :
265 4 : if ( !maThreads.empty() )
266 : {
267 : OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" );
268 0 : cancelAllJobs();
269 : }
270 :
271 4 : if ( mpCancelJobsThread != 0 )
272 : {
273 0 : if ( !mpCancelJobsThread->allJobsCancelled() )
274 : OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" );
275 :
276 0 : mpCancelJobsThread->stopWhenAllJobsCancelled();
277 0 : mpCancelJobsThread->join();
278 0 : delete mpCancelJobsThread;
279 0 : mpCancelJobsThread = 0;
280 : }
281 8 : }
282 :
283 : // com.sun.star.uno.XServiceInfo:
284 2 : OUString SAL_CALL FinalThreadManager::getImplementationName() throw (css::uno::RuntimeException, std::exception)
285 : {
286 2 : return comp_FinalThreadManager::_getImplementationName();
287 : }
288 :
289 0 : sal_Bool SAL_CALL FinalThreadManager::supportsService(OUString const & serviceName) throw (css::uno::RuntimeException, std::exception)
290 : {
291 0 : return cppu::supportsService(this, serviceName);
292 : }
293 :
294 0 : css::uno::Sequence< OUString > SAL_CALL FinalThreadManager::getSupportedServiceNames() throw (css::uno::RuntimeException, std::exception)
295 : {
296 0 : return comp_FinalThreadManager::_getSupportedServiceNames();
297 : }
298 :
299 : // ::com::sun::star::util::XJobManager:
300 8 : void SAL_CALL FinalThreadManager::registerJob(const css::uno::Reference< css::util::XCancellable > & Job) throw (css::uno::RuntimeException, std::exception)
301 : {
302 8 : osl::MutexGuard aGuard(maMutex);
303 :
304 8 : maThreads.push_back( Job );
305 :
306 8 : if ( !mbRegisteredAtDesktop )
307 : {
308 2 : registerAsListenerAtDesktop();
309 2 : mbRegisteredAtDesktop = true;
310 8 : }
311 8 : }
312 :
313 8 : void SAL_CALL FinalThreadManager::releaseJob(const css::uno::Reference< css::util::XCancellable > & Job) throw (css::uno::RuntimeException, std::exception)
314 : {
315 8 : osl::MutexGuard aGuard(maMutex);
316 :
317 8 : maThreads.remove( Job );
318 8 : }
319 :
320 2 : void SAL_CALL FinalThreadManager::cancelAllJobs() throw (css::uno::RuntimeException, std::exception)
321 : {
322 2 : std::list< css::uno::Reference< css::util::XCancellable > > aThreads;
323 : {
324 2 : osl::MutexGuard aGuard(maMutex);
325 :
326 2 : aThreads.insert( aThreads.end(), maThreads.begin(), maThreads.end() );
327 2 : maThreads.clear();
328 : }
329 :
330 2 : if ( !aThreads.empty() )
331 : {
332 0 : osl::MutexGuard aGuard(maMutex);
333 :
334 0 : if ( mpCancelJobsThread == 0 )
335 : {
336 0 : mpCancelJobsThread = new CancelJobsThread( aThreads );;
337 0 : if ( !mpCancelJobsThread->create() )
338 : {
339 0 : delete mpCancelJobsThread;
340 0 : mpCancelJobsThread = 0;
341 0 : while ( !aThreads.empty() )
342 : {
343 0 : aThreads.front()->cancel();
344 0 : aThreads.pop_front();
345 : }
346 : }
347 : }
348 : else
349 0 : mpCancelJobsThread->addJobs( aThreads );
350 2 : }
351 2 : }
352 :
353 : // ::com::sun::star::frame::XTerminateListener
354 2 : void SAL_CALL FinalThreadManager::queryTermination( const css::lang::EventObject& ) throw (css::frame::TerminationVetoException, css::uno::RuntimeException, std::exception)
355 : {
356 2 : osl::MutexGuard aGuard(maMutex);
357 :
358 2 : cancelAllJobs();
359 : // Sleep 1 second to give the thread for job cancellation some time.
360 : // Probably, all started threads have already finished its work.
361 2 : if ( mpCancelJobsThread != 0 &&
362 0 : !mpCancelJobsThread->allJobsCancelled() )
363 : {
364 : TimeValue aSleepTime;
365 0 : aSleepTime.Seconds = 1;
366 0 : aSleepTime.Nanosec = 0;
367 0 : osl_waitThread( &aSleepTime );
368 : }
369 :
370 2 : if ( mpCancelJobsThread != 0 &&
371 0 : !mpCancelJobsThread->allJobsCancelled() )
372 : {
373 0 : if ( mpTerminateOfficeThread != 0 )
374 : {
375 0 : if ( mpTerminateOfficeThread->isRunning() )
376 0 : mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
377 : else
378 0 : delete mpTerminateOfficeThread;
379 :
380 0 : mpTerminateOfficeThread = 0;
381 : }
382 : mpTerminateOfficeThread = new TerminateOfficeThread( *mpCancelJobsThread,
383 0 : m_xContext );
384 0 : if ( !mpTerminateOfficeThread->create() )
385 : {
386 0 : delete mpTerminateOfficeThread;
387 0 : mpTerminateOfficeThread = 0;
388 : }
389 :
390 0 : throw css::frame::TerminationVetoException();
391 : }
392 :
393 2 : mpPauseThreadStarting = new SwPauseThreadStarting();
394 :
395 2 : return;
396 : }
397 :
398 0 : void SAL_CALL FinalThreadManager::cancelTermination( const css::lang::EventObject& ) throw (css::uno::RuntimeException, std::exception)
399 : {
400 0 : if ( mpPauseThreadStarting != 0 )
401 : {
402 0 : delete mpPauseThreadStarting;
403 0 : mpPauseThreadStarting = 0;
404 : }
405 :
406 0 : return;
407 : }
408 :
409 2 : void SAL_CALL FinalThreadManager::notifyTermination( const css::lang::EventObject& ) throw (css::uno::RuntimeException, std::exception)
410 : {
411 2 : if ( mpTerminateOfficeThread != 0 )
412 : {
413 0 : if ( mpTerminateOfficeThread->isRunning() )
414 0 : mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
415 : else
416 0 : delete mpTerminateOfficeThread;
417 :
418 0 : mpTerminateOfficeThread = 0;
419 : }
420 :
421 2 : if ( !maThreads.empty() )
422 0 : cancelAllJobs();
423 :
424 2 : if ( mpCancelJobsThread != 0 )
425 : {
426 0 : mpCancelJobsThread->stopWhenAllJobsCancelled();
427 0 : mpCancelJobsThread->join();
428 0 : delete mpCancelJobsThread;
429 0 : mpCancelJobsThread = 0;
430 : }
431 :
432 : // get reference of this
433 2 : css::uno::Reference< css::uno::XInterface > aOwnRef( static_cast< cppu::OWeakObject* >( this ));
434 : // notify <SwThreadJoiner> to release its reference
435 2 : SwThreadJoiner::ReleaseThreadJoiner();
436 2 : }
437 :
438 : // ::com::sun:star::lang::XEventListener (inherited via com::sun::star::frame::XTerminateListener)
439 0 : void SAL_CALL FinalThreadManager::disposing( const css::lang::EventObject& ) throw (css::uno::RuntimeException, std::exception)
440 : {
441 : // nothing to do, because instance doesn't hold any references of observed objects
442 0 : }
443 :
444 : // component helper namespace
445 : namespace comp_FinalThreadManager {
446 :
447 10 : OUString SAL_CALL _getImplementationName()
448 : {
449 10 : return OUString("com.sun.star.util.comp.FinalThreadManager");
450 : }
451 :
452 4 : css::uno::Sequence< OUString > SAL_CALL _getSupportedServiceNames()
453 : {
454 4 : css::uno::Sequence< OUString > s(1);
455 4 : s[0] = "com.sun.star.util.JobManager";
456 4 : return s;
457 : }
458 :
459 4 : css::uno::Reference< css::uno::XInterface > SAL_CALL _create(
460 : const css::uno::Reference< css::uno::XComponentContext > & context)
461 : {
462 4 : return static_cast< ::cppu::OWeakObject * >(new FinalThreadManager(context));
463 : }
464 :
465 : } // closing component helper namespace
466 :
467 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|