LibreOffice Module sw (master)  1
finalthreadmanager.cxx
Go to the documentation of this file.
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/diagnose.h>
23 #include <osl/thread.hxx>
24 #include <pausethreadstarting.hxx>
25 #include <swthreadjoiner.hxx>
26 
27 #include <com/sun/star/frame/Desktop.hpp>
28 #include <com/sun/star/frame/TerminationVetoException.hpp>
29 #include <rtl/ustring.hxx>
31 #include <mutex>
32 
37 class CancelJobsThread : public osl::Thread
38 {
39  public:
40  explicit CancelJobsThread( std::list< css::uno::Reference< css::util::XCancellable > >&& rJobs )
41  : maJobs( std::move(rJobs) ),
42  mbAllJobsCancelled( false ),
43  mbStopped( false )
44  {
45  }
46 
47  void addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs );
48  bool allJobsCancelled() const;
50 
51  private:
52  bool existJobs() const;
53 
54  css::uno::Reference< css::util::XCancellable > getNextJob();
55 
56  bool stopped() const;
57  virtual void SAL_CALL run() override;
58  mutable std::mutex maMutex;
59 
60  std::list< css::uno::Reference< css::util::XCancellable > > maJobs;
61 
63  bool mbStopped;
64 };
65 
66 void CancelJobsThread::addJobs( std::list< css::uno::Reference< css::util::XCancellable > >& rJobs )
67 {
68  std::scoped_lock aGuard(maMutex);
69 
70  maJobs.insert( maJobs.end(), rJobs.begin(), rJobs.end() );
71  mbAllJobsCancelled = !maJobs.empty();
72 }
73 
75 {
76  std::scoped_lock aGuard(maMutex);
77 
78  return !maJobs.empty();
79 }
80 
82 {
83  std::scoped_lock aGuard(maMutex);
84 
85  return maJobs.empty() && mbAllJobsCancelled;
86 }
87 
89 {
90  std::scoped_lock aGuard(maMutex);
91 
92  mbStopped = true;
93 }
94 
95 css::uno::Reference< css::util::XCancellable > CancelJobsThread::getNextJob()
96 {
97  css::uno::Reference< css::util::XCancellable > xRet;
98 
99  {
100  std::scoped_lock aGuard(maMutex);
101 
102  if ( !maJobs.empty() )
103  {
104  xRet = maJobs.front();
105  maJobs.pop_front();
106  }
107  }
108 
109  return xRet;
110 }
111 
113 {
114  std::scoped_lock aGuard(maMutex);
115 
116  return mbStopped;
117 }
118 
119 void SAL_CALL CancelJobsThread::run()
120 {
121  osl_setThreadName("sw CancelJobsThread");
122 
123  while ( !stopped() )
124  {
125  while ( existJobs() )
126  {
127  css::uno::Reference< css::util::XCancellable > aJob( getNextJob() );
128  if ( aJob.is() )
129  aJob->cancel();
130  }
131 
132  mbAllJobsCancelled = true;
133 
134  {
135  osl::Thread::wait(std::chrono::seconds(1));
136  }
137  }
138 }
139 
144 class TerminateOfficeThread : public osl::Thread
145 {
146  public:
147  TerminateOfficeThread( CancelJobsThread const & rCancelJobsThread,
148  css::uno::Reference< css::uno::XComponentContext > const & xContext )
149  : mrCancelJobsThread( rCancelJobsThread ),
150  mbStopOfficeTermination( false ),
151  mxContext( xContext )
152  {
153  }
154 
155  void StopOfficeTermination();
156 
157  private:
158  virtual void SAL_CALL run() override;
159  virtual void SAL_CALL onTerminated() override;
162 
163  osl::Mutex maMutex;
164 
167 
168  css::uno::Reference< css::uno::XComponentContext > mxContext;
169 };
170 
172 {
173  osl::MutexGuard aGuard(maMutex);
174 
176 }
177 
179 {
180  osl::MutexGuard aGuard(maMutex);
181 
183 }
184 
186 {
187  osl_setThreadName("sw TerminateOfficeThread");
188 
189  while ( !OfficeTerminationStopped() )
190  {
191  osl::MutexGuard aGuard(maMutex);
192 
194  break;
195  }
196 
197  if ( !OfficeTerminationStopped() )
199 }
200 
202 {
203  css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(mxContext);
204 
205  css::uno::Reference< css::container::XElementAccess > xList = xDesktop->getFrames();
206  if ( !xList.is() )
207  {
208  OSL_FAIL( "<TerminateOfficeThread::PerformOfficeTermination()> - no XElementAccess!" );
209  return;
210  }
211 
212  if ( !xList->hasElements() )
213  {
214  if ( !OfficeTerminationStopped() )
215  xDesktop->terminate();
216  }
217 }
218 
220 {
221  if ( OfficeTerminationStopped() )
222  delete this;
223 }
224 
225 FinalThreadManager::FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const & context)
226  : m_xContext(context),
227  mpTerminateOfficeThread( nullptr ),
228  mbRegisteredAtDesktop( false )
229 {
230 
231 }
232 
234 {
235  css::uno::Reference< css::frame::XDesktop2 > xDesktop = css::frame::Desktop::create(m_xContext);
236  xDesktop->addTerminateListener( css::uno::Reference< css::frame::XTerminateListener >( this ) );
237 }
238 
240 {
241  if ( mpPauseThreadStarting )
242  {
243  mpPauseThreadStarting.reset();
244  }
245 
246  if ( mpTerminateOfficeThread != nullptr )
247  {
248  mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
249  mpTerminateOfficeThread = nullptr;
250  }
251 
252  if ( !maThreads.empty() )
253  {
254  OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - still registered jobs are existing -> perform cancellation" );
255  cancelAllJobs();
256  }
257 
258  if ( mpCancelJobsThread != nullptr )
259  {
260  if ( !mpCancelJobsThread->allJobsCancelled() )
261  OSL_FAIL( "<FinalThreadManager::~FinalThreadManager()> - cancellation of registered jobs not yet finished -> wait for its finish" );
262 
263  mpCancelJobsThread->stopWhenAllJobsCancelled();
264  mpCancelJobsThread->join();
265  mpCancelJobsThread.reset();
266  }
267 }
268 
269 // com.sun.star.uno.XServiceInfo:
271 {
272  return "com.sun.star.util.comp.FinalThreadManager";
273 }
274 
275 sal_Bool SAL_CALL FinalThreadManager::supportsService(OUString const & serviceName)
276 {
277  return cppu::supportsService(this, serviceName);
278 }
279 
280 css::uno::Sequence< OUString > SAL_CALL FinalThreadManager::getSupportedServiceNames()
281 {
282  return { "com.sun.star.util.JobManager" };
283 }
284 
285 // css::util::XJobManager:
286 void SAL_CALL FinalThreadManager::registerJob(const css::uno::Reference< css::util::XCancellable > & Job)
287 {
288  osl::MutexGuard aGuard(maMutex);
289 
290  maThreads.push_back( Job );
291 
292  if ( !mbRegisteredAtDesktop )
293  {
295  mbRegisteredAtDesktop = true;
296  }
297 }
298 
299 void SAL_CALL FinalThreadManager::releaseJob(const css::uno::Reference< css::util::XCancellable > & Job)
300 {
301  osl::MutexGuard aGuard(maMutex);
302 
303  maThreads.remove( Job );
304 }
305 
307 {
308  std::list< css::uno::Reference< css::util::XCancellable > > aThreads;
309  {
310  osl::MutexGuard aGuard(maMutex);
311 
312  aThreads.insert( aThreads.end(), maThreads.begin(), maThreads.end() );
313  maThreads.clear();
314  }
315 
316  if ( aThreads.empty() )
317  return;
318 
319  osl::MutexGuard aGuard(maMutex);
320 
321  if ( mpCancelJobsThread == nullptr )
322  {
323  mpCancelJobsThread.reset(new CancelJobsThread( std::list(aThreads) ));
324  if ( !mpCancelJobsThread->create() )
325  {
326  mpCancelJobsThread.reset();
327  for (auto const& elem : aThreads)
328  {
329  elem->cancel();
330  }
331  aThreads.clear();
332  }
333  }
334  else
335  mpCancelJobsThread->addJobs( aThreads );
336 }
337 
338 // css::frame::XTerminateListener
339 void SAL_CALL FinalThreadManager::queryTermination( const css::lang::EventObject& )
340 {
341  osl::MutexGuard aGuard(maMutex);
342 
343  cancelAllJobs();
344  // Sleep 1 second to give the thread for job cancellation some time.
345  // Probably, all started threads have already finished its work.
346  if ( mpCancelJobsThread != nullptr &&
347  !mpCancelJobsThread->allJobsCancelled() )
348  {
349  osl::Thread::wait(std::chrono::seconds(1));
350  }
351 
352  if ( mpCancelJobsThread != nullptr &&
353  !mpCancelJobsThread->allJobsCancelled() )
354  {
355  if ( mpTerminateOfficeThread != nullptr )
356  {
357  if ( mpTerminateOfficeThread->isRunning() )
358  mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
359  else
361 
362  mpTerminateOfficeThread = nullptr;
363  }
365  m_xContext );
366  if ( !mpTerminateOfficeThread->create() )
367  {
369  mpTerminateOfficeThread = nullptr;
370  }
371 
372  throw css::frame::TerminationVetoException();
373  }
374 
376 }
377 
378 void SAL_CALL FinalThreadManager::cancelTermination( const css::lang::EventObject& )
379 {
380  mpPauseThreadStarting.reset();
381 }
382 
383 void SAL_CALL FinalThreadManager::notifyTermination( const css::lang::EventObject& )
384 {
385  if ( mpTerminateOfficeThread != nullptr )
386  {
387  if ( mpTerminateOfficeThread->isRunning() )
388  mpTerminateOfficeThread->StopOfficeTermination(); // thread kills itself.
389  else
391 
392  mpTerminateOfficeThread = nullptr;
393  }
394 
395  if ( !maThreads.empty() )
396  cancelAllJobs();
397 
398  if ( mpCancelJobsThread != nullptr )
399  {
400  mpCancelJobsThread->stopWhenAllJobsCancelled();
401  mpCancelJobsThread->join();
402  mpCancelJobsThread.reset();
403  }
404 
405  // get reference of this
406  css::uno::Reference< css::uno::XInterface > aOwnRef( static_cast< cppu::OWeakObject* >( this ));
407  // notify <SwThreadJoiner> to release its reference
409 }
410 
411 // ::com::sun:star::lang::XEventListener (inherited via css::frame::XTerminateListener)
412 void SAL_CALL FinalThreadManager::disposing( const css::lang::EventObject& )
413 {
414  // nothing to do, because instance doesn't hold any references of observed objects
415 }
416 
417 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
419  css::uno::Sequence<css::uno::Any> const &)
420 {
421  return cppu::acquire(new FinalThreadManager(context));
422 }
423 
424 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::list< css::uno::Reference< css::util::XCancellable > > maThreads
virtual void SAL_CALL run() override
thread to cancel a give list of cancellable jobs
css::uno::Reference< css::util::XCancellable > getNextJob()
virtual OUString SAL_CALL getImplementationName() override
css::uno::Reference< css::uno::XComponentContext > mxContext
TerminateOfficeThread * mpTerminateOfficeThread
std::unique_ptr< CancelJobsThread > mpCancelJobsThread
CancelJobsThread(std::list< css::uno::Reference< css::util::XCancellable > > &&rJobs)
void ReleaseThreadJoiner()
Job
virtual void SAL_CALL run() override
virtual ~FinalThreadManager() override
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
std::unique_ptr< SwPauseThreadStarting, o3tl::default_delete< SwPauseThreadStarting > > mpPauseThreadStarting
std::list< css::uno::Reference< css::util::XCancellable > > maJobs
virtual void SAL_CALL registerJob(const css::uno::Reference< css::util::XCancellable > &Job) override
virtual void SAL_CALL cancelTermination(const css::lang::EventObject &Event) override
virtual void SAL_CALL notifyTermination(const css::lang::EventObject &Event) override
FinalThreadManager(css::uno::Reference< css::uno::XComponentContext > const &context)
css::uno::Reference< css::uno::XComponentContext > m_xContext
Helper class to pause starting of threads during existence of an instance of this class...
virtual void SAL_CALL disposing(const css::lang::EventObject &Source) override
virtual void SAL_CALL cancelAllJobs() override
virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override
unsigned char sal_Bool
void addJobs(std::list< css::uno::Reference< css::util::XCancellable > > &rJobs)
thread to terminate office, when all jobs are cancelled.
TerminateOfficeThread(CancelJobsThread const &rCancelJobsThread, css::uno::Reference< css::uno::XComponentContext > const &xContext)
const CancelJobsThread & mrCancelJobsThread
virtual sal_Bool SAL_CALL supportsService(const OUString &ServiceName) override
bool existJobs() const
virtual void SAL_CALL onTerminated() override
virtual void SAL_CALL queryTermination(const css::lang::EventObject &Event) override
virtual void SAL_CALL releaseJob(const css::uno::Reference< css::util::XCancellable > &Job) override
Reference< XComponentContext > m_xContext
bool allJobsCancelled() const
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_util_comp_FinalThreadManager_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &)