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