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