1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <finalthreadmanager.hxx>
22 #include <osl/diagnose.h>
23 #include <osl/thread.hxx>
24 #include <pausethreadstarting.hxx>
25 #include <swthreadjoiner.hxx>
27 #include <com/sun/star/frame/Desktop.hpp>
28 #include <com/sun/star/frame/TerminationVetoException.hpp>
29 #include <rtl/ustring.hxx>
30 #include <cppuhelper/supportsservice.hxx>
35 /** thread to cancel a give list of cancellable jobs
37 helper class for FinalThreadManager
39 class CancelJobsThread
: public osl::Thread
42 explicit CancelJobsThread( std::list
< css::uno::Reference
< css::util::XCancellable
> >&& rJobs
)
43 : maJobs( std::move(rJobs
) ),
44 mbAllJobsCancelled( false ),
49 void addJobs( std::list
< css::uno::Reference
< css::util::XCancellable
> >& rJobs
);
50 bool allJobsCancelled() const;
51 void stopWhenAllJobsCancelled();
54 bool existJobs() const;
56 css::uno::Reference
< css::util::XCancellable
> getNextJob();
59 virtual void SAL_CALL
run() override
;
60 mutable std::mutex maMutex
;
62 std::list
< css::uno::Reference
< css::util::XCancellable
> > maJobs
;
64 bool mbAllJobsCancelled
;
68 void CancelJobsThread::addJobs( std::list
< css::uno::Reference
< css::util::XCancellable
> >& rJobs
)
70 std::scoped_lock
aGuard(maMutex
);
72 maJobs
.insert( maJobs
.end(), rJobs
.begin(), rJobs
.end() );
73 mbAllJobsCancelled
= !maJobs
.empty();
76 bool CancelJobsThread::existJobs() const
78 std::scoped_lock
aGuard(maMutex
);
80 return !maJobs
.empty();
83 bool CancelJobsThread::allJobsCancelled() const
85 std::scoped_lock
aGuard(maMutex
);
87 return maJobs
.empty() && mbAllJobsCancelled
;
90 void CancelJobsThread::stopWhenAllJobsCancelled()
92 std::scoped_lock
aGuard(maMutex
);
97 css::uno::Reference
< css::util::XCancellable
> CancelJobsThread::getNextJob()
99 css::uno::Reference
< css::util::XCancellable
> xRet
;
102 std::scoped_lock
aGuard(maMutex
);
104 if ( !maJobs
.empty() )
106 xRet
= maJobs
.front();
114 bool CancelJobsThread::stopped() const
116 std::scoped_lock
aGuard(maMutex
);
121 void SAL_CALL
CancelJobsThread::run()
123 osl_setThreadName("sw CancelJobsThread");
127 while ( existJobs() )
129 css::uno::Reference
< css::util::XCancellable
> aJob( getNextJob() );
134 mbAllJobsCancelled
= true;
137 std::this_thread::sleep_for(std::chrono::seconds(1));
142 /** thread to terminate office, when all jobs are cancelled.
144 helper class for FinalThreadManager
146 class TerminateOfficeThread
: public osl::Thread
149 TerminateOfficeThread( CancelJobsThread
const & rCancelJobsThread
,
150 css::uno::Reference
< css::uno::XComponentContext
> xContext
)
151 : mrCancelJobsThread( rCancelJobsThread
),
152 mbStopOfficeTermination( false ),
153 mxContext(std::move( xContext
))
157 void StopOfficeTermination();
160 virtual void SAL_CALL
run() override
;
161 virtual void SAL_CALL
onTerminated() override
;
162 bool OfficeTerminationStopped();
163 void PerformOfficeTermination();
167 const CancelJobsThread
& mrCancelJobsThread
;
168 bool mbStopOfficeTermination
;
170 css::uno::Reference
< css::uno::XComponentContext
> mxContext
;
173 void TerminateOfficeThread::StopOfficeTermination()
175 osl::MutexGuard
aGuard(maMutex
);
177 mbStopOfficeTermination
= true;
180 bool TerminateOfficeThread::OfficeTerminationStopped()
182 osl::MutexGuard
aGuard(maMutex
);
184 return mbStopOfficeTermination
;
187 void SAL_CALL
TerminateOfficeThread::run()
189 osl_setThreadName("sw TerminateOfficeThread");
191 while ( !OfficeTerminationStopped() )
193 osl::MutexGuard
aGuard(maMutex
);
195 if ( mrCancelJobsThread
.allJobsCancelled() )
199 if ( !OfficeTerminationStopped() )
200 PerformOfficeTermination();
203 void TerminateOfficeThread::PerformOfficeTermination()
205 css::uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create(mxContext
);
207 css::uno::Reference
< css::container::XElementAccess
> xList
= xDesktop
->getFrames();
210 OSL_FAIL( "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" );
214 if ( !xList
->hasElements() )
216 if ( !OfficeTerminationStopped() )
217 xDesktop
->terminate();
221 void SAL_CALL
TerminateOfficeThread::onTerminated()
223 if ( OfficeTerminationStopped() )
227 FinalThreadManager::FinalThreadManager(css::uno::Reference
< css::uno::XComponentContext
> context
)
228 : m_xContext(std::move(context
)),
229 mpTerminateOfficeThread( nullptr ),
230 mbRegisteredAtDesktop( false )
235 void FinalThreadManager::registerAsListenerAtDesktop()
237 css::uno::Reference
< css::frame::XDesktop2
> xDesktop
= css::frame::Desktop::create(m_xContext
);
238 xDesktop
->addTerminateListener( css::uno::Reference
< css::frame::XTerminateListener
>( this ) );
241 FinalThreadManager::~FinalThreadManager()
243 if ( mpPauseThreadStarting
)
245 mpPauseThreadStarting
.reset();
248 if ( mpTerminateOfficeThread
!= nullptr )
250 mpTerminateOfficeThread
->StopOfficeTermination(); // thread kills itself.
251 mpTerminateOfficeThread
= nullptr;
254 if ( !maThreads
.empty() )
256 OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" );
260 if ( mpCancelJobsThread
!= nullptr )
262 if ( !mpCancelJobsThread
->allJobsCancelled() )
263 OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" );
265 mpCancelJobsThread
->stopWhenAllJobsCancelled();
266 mpCancelJobsThread
->join();
267 mpCancelJobsThread
.reset();
271 // com.sun.star.uno.XServiceInfo:
272 OUString SAL_CALL
FinalThreadManager::getImplementationName()
274 return "com.sun.star.util.comp.FinalThreadManager";
277 sal_Bool SAL_CALL
FinalThreadManager::supportsService(OUString
const & serviceName
)
279 return cppu::supportsService(this, serviceName
);
282 css::uno::Sequence
< OUString
> SAL_CALL
FinalThreadManager::getSupportedServiceNames()
284 return { "com.sun.star.util.JobManager" };
287 // css::util::XJobManager:
288 void SAL_CALL
FinalThreadManager::registerJob(const css::uno::Reference
< css::util::XCancellable
> & Job
)
290 osl::MutexGuard
aGuard(maMutex
);
292 maThreads
.push_back( Job
);
294 if ( !mbRegisteredAtDesktop
)
296 registerAsListenerAtDesktop();
297 mbRegisteredAtDesktop
= true;
301 void SAL_CALL
FinalThreadManager::releaseJob(const css::uno::Reference
< css::util::XCancellable
> & Job
)
303 osl::MutexGuard
aGuard(maMutex
);
305 maThreads
.remove( Job
);
308 void SAL_CALL
FinalThreadManager::cancelAllJobs()
310 std::list
< css::uno::Reference
< css::util::XCancellable
> > aThreads
;
312 osl::MutexGuard
aGuard(maMutex
);
314 aThreads
.insert( aThreads
.end(), maThreads
.begin(), maThreads
.end() );
318 if ( aThreads
.empty() )
321 osl::MutexGuard
aGuard(maMutex
);
323 if ( mpCancelJobsThread
== nullptr )
325 mpCancelJobsThread
.reset(new CancelJobsThread( std::list(aThreads
) ));
326 if ( !mpCancelJobsThread
->create() )
328 mpCancelJobsThread
.reset();
329 for (auto const& elem
: aThreads
)
337 mpCancelJobsThread
->addJobs( aThreads
);
340 // css::frame::XTerminateListener
341 void SAL_CALL
FinalThreadManager::queryTermination( const css::lang::EventObject
& )
343 osl::MutexGuard
aGuard(maMutex
);
346 // Sleep 1 second to give the thread for job cancellation some time.
347 // Probably, all started threads have already finished its work.
348 if ( mpCancelJobsThread
!= nullptr &&
349 !mpCancelJobsThread
->allJobsCancelled() )
351 std::this_thread::sleep_for(std::chrono::seconds(1));
354 if ( mpCancelJobsThread
!= nullptr &&
355 !mpCancelJobsThread
->allJobsCancelled() )
357 if ( mpTerminateOfficeThread
!= nullptr )
359 if ( mpTerminateOfficeThread
->isRunning() )
360 mpTerminateOfficeThread
->StopOfficeTermination(); // thread kills itself.
362 delete mpTerminateOfficeThread
;
364 mpTerminateOfficeThread
= nullptr;
366 mpTerminateOfficeThread
= new TerminateOfficeThread( *mpCancelJobsThread
,
368 if ( !mpTerminateOfficeThread
->create() )
370 delete mpTerminateOfficeThread
;
371 mpTerminateOfficeThread
= nullptr;
374 throw css::frame::TerminationVetoException();
377 mpPauseThreadStarting
.reset(new SwPauseThreadStarting());
380 void SAL_CALL
FinalThreadManager::cancelTermination( const css::lang::EventObject
& )
382 mpPauseThreadStarting
.reset();
385 void SAL_CALL
FinalThreadManager::notifyTermination( const css::lang::EventObject
& )
387 if ( mpTerminateOfficeThread
!= nullptr )
389 if ( mpTerminateOfficeThread
->isRunning() )
390 mpTerminateOfficeThread
->StopOfficeTermination(); // thread kills itself.
392 delete mpTerminateOfficeThread
;
394 mpTerminateOfficeThread
= nullptr;
397 if ( !maThreads
.empty() )
400 if ( mpCancelJobsThread
!= nullptr )
402 mpCancelJobsThread
->stopWhenAllJobsCancelled();
403 mpCancelJobsThread
->join();
404 mpCancelJobsThread
.reset();
407 // get reference of this
408 css::uno::Reference
< css::uno::XInterface
> aOwnRef( static_cast< cppu::OWeakObject
* >( this ));
409 // notify <SwThreadJoiner> to release its reference
410 SwThreadJoiner::ReleaseThreadJoiner();
413 // ::com::sun:star::lang::XEventListener (inherited via css::frame::XTerminateListener)
414 void SAL_CALL
FinalThreadManager::disposing( const css::lang::EventObject
& )
416 // nothing to do, because instance doesn't hold any references of observed objects
419 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
420 com_sun_star_util_comp_FinalThreadManager_get_implementation(css::uno::XComponentContext
* context
,
421 css::uno::Sequence
<css::uno::Any
> const &)
423 return cppu::acquire(new FinalThreadManager(context
));
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */