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/Desktop.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 0 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(mxContext);
209 :
210 0 : css::uno::Reference< css::container::XElementAccess > xList( xDesktop->getFrames(), css::uno::UNO_QUERY );
211 0 : if ( !xList.is() )
212 : {
213 : OSL_FAIL( "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" );
214 0 : return;
215 : }
216 :
217 0 : if ( !xList->hasElements() )
218 : {
219 0 : if ( !OfficeTerminationStopped() )
220 0 : xDesktop->terminate();
221 0 : }
222 : }
223 :
224 0 : void SAL_CALL TerminateOfficeThread::onTerminated()
225 : {
226 0 : if ( OfficeTerminationStopped() )
227 0 : delete this;
228 0 : }
229 :
230 :
231 : /** class FinalThreadManager
232 :
233 : @author OD
234 : */
235 0 : FinalThreadManager::FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const & context)
236 : : m_xContext(context),
237 : maMutex(),
238 : maThreads(),
239 : mpCancelJobsThread( 0 ),
240 : mpTerminateOfficeThread( 0 ),
241 : mpPauseThreadStarting( 0 ),
242 0 : mbRegisteredAtDesktop( false )
243 : {
244 :
245 0 : }
246 :
247 0 : void FinalThreadManager::registerAsListenerAtDesktop()
248 : {
249 0 : css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
250 0 : xDesktop->addTerminateListener( css::uno::Reference< css::frame::XTerminateListener >( static_cast< cppu::OWeakObject* >( this ), css::uno::UNO_QUERY ) );
251 0 : }
252 :
253 0 : FinalThreadManager::~FinalThreadManager()
254 : {
255 0 : if ( mpPauseThreadStarting != 0 )
256 : {
257 0 : delete mpPauseThreadStarting;
258 0 : mpPauseThreadStarting = 0;
259 : }
260 :
261 0 : if ( mpTerminateOfficeThread != 0 )
262 : {
263 0 : mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
264 0 : mpTerminateOfficeThread = 0;
265 : }
266 :
267 0 : if ( !maThreads.empty() )
268 : {
269 : OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" );
270 0 : cancelAllJobs();
271 : }
272 :
273 0 : if ( mpCancelJobsThread != 0 )
274 : {
275 0 : if ( !mpCancelJobsThread->allJobsCancelled() )
276 : OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" );
277 :
278 0 : mpCancelJobsThread->stopWhenAllJobsCancelled();
279 0 : mpCancelJobsThread->join();
280 0 : delete mpCancelJobsThread;
281 0 : mpCancelJobsThread = 0;
282 : }
283 0 : }
284 :
285 : // com.sun.star.uno.XServiceInfo:
286 0 : ::rtl::OUString SAL_CALL FinalThreadManager::getImplementationName() throw (css::uno::RuntimeException)
287 : {
288 0 : return comp_FinalThreadManager::_getImplementationName();
289 : }
290 :
291 0 : ::sal_Bool SAL_CALL FinalThreadManager::supportsService(::rtl::OUString const & serviceName) throw (css::uno::RuntimeException)
292 : {
293 0 : css::uno::Sequence< ::rtl::OUString > serviceNames = comp_FinalThreadManager::_getSupportedServiceNames();
294 0 : for (::sal_Int32 i = 0; i < serviceNames.getLength(); ++i) {
295 0 : if (serviceNames[i] == serviceName)
296 0 : return sal_True;
297 : }
298 0 : return sal_False;
299 : }
300 :
301 0 : css::uno::Sequence< ::rtl::OUString > SAL_CALL FinalThreadManager::getSupportedServiceNames() throw (css::uno::RuntimeException)
302 : {
303 0 : return comp_FinalThreadManager::_getSupportedServiceNames();
304 : }
305 :
306 : // ::com::sun::star::util::XJobManager:
307 0 : void SAL_CALL FinalThreadManager::registerJob(const css::uno::Reference< css::util::XCancellable > & Job) throw (css::uno::RuntimeException)
308 : {
309 0 : osl::MutexGuard aGuard(maMutex);
310 :
311 0 : maThreads.push_back( Job );
312 :
313 0 : if ( !mbRegisteredAtDesktop )
314 : {
315 0 : registerAsListenerAtDesktop();
316 0 : mbRegisteredAtDesktop = true;
317 0 : }
318 0 : }
319 :
320 0 : void SAL_CALL FinalThreadManager::releaseJob(const css::uno::Reference< css::util::XCancellable > & Job) throw (css::uno::RuntimeException)
321 : {
322 0 : osl::MutexGuard aGuard(maMutex);
323 :
324 0 : maThreads.remove( Job );
325 0 : }
326 :
327 0 : void SAL_CALL FinalThreadManager::cancelAllJobs() throw (css::uno::RuntimeException)
328 : {
329 0 : std::list< css::uno::Reference< css::util::XCancellable > > aThreads;
330 : {
331 0 : osl::MutexGuard aGuard(maMutex);
332 :
333 0 : aThreads.insert( aThreads.end(), maThreads.begin(), maThreads.end() );
334 0 : maThreads.clear();
335 : }
336 :
337 0 : if ( !aThreads.empty() )
338 : {
339 0 : osl::MutexGuard aGuard(maMutex);
340 :
341 0 : if ( mpCancelJobsThread == 0 )
342 : {
343 0 : mpCancelJobsThread = new CancelJobsThread( aThreads );;
344 0 : if ( !mpCancelJobsThread->create() )
345 : {
346 0 : delete mpCancelJobsThread;
347 0 : mpCancelJobsThread = 0;
348 0 : while ( !aThreads.empty() )
349 : {
350 0 : aThreads.front()->cancel();
351 0 : aThreads.pop_front();
352 : }
353 : }
354 : }
355 : else
356 0 : mpCancelJobsThread->addJobs( aThreads );
357 0 : }
358 0 : }
359 :
360 : // ::com::sun::star::frame::XTerminateListener
361 0 : void SAL_CALL FinalThreadManager::queryTermination( const css::lang::EventObject& ) throw (css::frame::TerminationVetoException, css::uno::RuntimeException)
362 : {
363 0 : osl::MutexGuard aGuard(maMutex);
364 :
365 0 : cancelAllJobs();
366 : // Sleep 1 second to give the thread for job cancellation some time.
367 : // Probably, all started threads have already finished its work.
368 0 : if ( mpCancelJobsThread != 0 &&
369 0 : !mpCancelJobsThread->allJobsCancelled() )
370 : {
371 : TimeValue aSleepTime;
372 0 : aSleepTime.Seconds = 1;
373 0 : aSleepTime.Nanosec = 0;
374 0 : osl_waitThread( &aSleepTime );
375 : }
376 :
377 0 : if ( mpCancelJobsThread != 0 &&
378 0 : !mpCancelJobsThread->allJobsCancelled() )
379 : {
380 0 : if ( mpTerminateOfficeThread != 0 )
381 : {
382 0 : if ( mpTerminateOfficeThread->isRunning() )
383 0 : mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
384 : else
385 0 : delete mpTerminateOfficeThread;
386 :
387 0 : mpTerminateOfficeThread = 0;
388 : }
389 : mpTerminateOfficeThread = new TerminateOfficeThread( *mpCancelJobsThread,
390 0 : m_xContext );
391 0 : if ( !mpTerminateOfficeThread->create() )
392 : {
393 0 : delete mpTerminateOfficeThread;
394 0 : mpTerminateOfficeThread = 0;
395 : }
396 :
397 0 : throw css::frame::TerminationVetoException();
398 : }
399 :
400 0 : mpPauseThreadStarting = new SwPauseThreadStarting();
401 :
402 0 : return;
403 : }
404 :
405 0 : void SAL_CALL FinalThreadManager::cancelTermination( const css::lang::EventObject& ) throw (css::uno::RuntimeException)
406 : {
407 0 : if ( mpPauseThreadStarting != 0 )
408 : {
409 0 : delete mpPauseThreadStarting;
410 0 : mpPauseThreadStarting = 0;
411 : }
412 :
413 0 : return;
414 : }
415 :
416 0 : void SAL_CALL FinalThreadManager::notifyTermination( const css::lang::EventObject& ) throw (css::uno::RuntimeException)
417 : {
418 0 : if ( mpTerminateOfficeThread != 0 )
419 : {
420 0 : if ( mpTerminateOfficeThread->isRunning() )
421 0 : mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
422 : else
423 0 : delete mpTerminateOfficeThread;
424 :
425 0 : mpTerminateOfficeThread = 0;
426 : }
427 :
428 0 : if ( !maThreads.empty() )
429 0 : cancelAllJobs();
430 :
431 0 : if ( mpCancelJobsThread != 0 )
432 : {
433 0 : mpCancelJobsThread->stopWhenAllJobsCancelled();
434 0 : mpCancelJobsThread->join();
435 0 : delete mpCancelJobsThread;
436 0 : mpCancelJobsThread = 0;
437 : }
438 :
439 : // get reference of this
440 0 : css::uno::Reference< css::uno::XInterface > aOwnRef( static_cast< cppu::OWeakObject* >( this ));
441 : // notify <SwThreadJoiner> to release its reference
442 0 : SwThreadJoiner::ReleaseThreadJoiner();
443 0 : }
444 :
445 : // ::com::sun:star::lang::XEventListener (inherited via com::sun::star::frame::XTerminateListener)
446 0 : void SAL_CALL FinalThreadManager::disposing( const css::lang::EventObject& ) throw (css::uno::RuntimeException)
447 : {
448 : // nothing to do, because instance doesn't hold any references of observed objects
449 0 : }
450 :
451 : // component helper namespace
452 : namespace comp_FinalThreadManager {
453 :
454 0 : ::rtl::OUString SAL_CALL _getImplementationName()
455 : {
456 : return ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
457 0 : "com.sun.star.util.comp.FinalThreadManager"));
458 : }
459 :
460 0 : css::uno::Sequence< ::rtl::OUString > SAL_CALL _getSupportedServiceNames()
461 : {
462 0 : css::uno::Sequence< ::rtl::OUString > s(1);
463 0 : s[0] = ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(
464 0 : "com.sun.star.util.JobManager"));
465 0 : return s;
466 : }
467 :
468 0 : css::uno::Reference< css::uno::XInterface > SAL_CALL _create(
469 : const css::uno::Reference< css::uno::XComponentContext > & context)
470 : SAL_THROW((css::uno::Exception))
471 : {
472 0 : return static_cast< ::cppu::OWeakObject * >(new FinalThreadManager(context));
473 : }
474 :
475 : } // closing component helper namespace
476 :
477 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|