LibreOffice Module sw (master)  1
threadmanager.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 "cancellablejob.hxx"
21 #include "threadmanager.hxx"
22 #include <threadlistener.hxx>
23 
24 #include <osl/diagnose.h>
25 
26 #include <algorithm>
27 
28 #include <com/sun/star/util/XJobManager.hpp>
29 
30 using namespace ::com::sun::star;
31 
36 const std::deque< ThreadManager::tThreadData >::size_type ThreadManager::snStartedSize = 10;
37 
38 ThreadManager::ThreadManager( uno::Reference< util::XJobManager > const & rThreadJoiner )
39  : mrThreadJoiner( rThreadJoiner ),
40  mnThreadIDCounter( 0 ),
41  maStartNewThreadIdle("SW ThreadManager StartNewThreadIdle"),
42  mbStartingOfThreadsSuspended( false )
43 {
44 }
45 
47 {
48  mpThreadListener = std::make_shared<ThreadListener>( *this );
49 
50  maStartNewThreadIdle.SetPriority( TaskPriority::LOWEST );
51  maStartNewThreadIdle.SetInvokeHandler( LINK( this, ThreadManager, TryToStartNewThread ) );
52 }
53 
55 {
57  maStartedThreads.clear();
58 }
59 
60 std::weak_ptr< IFinishedThreadListener > ThreadManager::GetThreadListenerWeakRef() const
61 {
62  return mpThreadListener;
63 }
64 
65 void ThreadManager::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
66 {
67  RemoveThread( nThreadID, true );
68 }
69 
70 oslInterlockedCount ThreadManager::AddThread(
71  const rtl::Reference< ObservableThread >& rThread )
72 
73 {
74  osl::MutexGuard aGuard(maMutex);
75 
76  // create new thread
77  tThreadData aThreadData;
78  oslInterlockedCount nNewThreadID( osl_atomic_increment( &mnThreadIDCounter ) );
79  {
80  aThreadData.nThreadID = nNewThreadID;
81 
82  aThreadData.pThread = rThread;
83  aThreadData.aJob = new CancellableJob( aThreadData.pThread );
84 
85  aThreadData.pThread->setPriority( osl_Thread_PriorityBelowNormal );
86  mpThreadListener->ListenToThread( aThreadData.nThreadID,
87  *(aThreadData.pThread) );
88  }
89 
90  // add thread to manager
91  if ( maStartedThreads.size() < snStartedSize &&
93  {
94  // Try to start thread
95  if ( !StartThread( aThreadData ) )
96  {
97  // No success on starting thread
98  // If no more started threads exist, but still threads are waiting,
99  // setup Timer to start thread from waiting ones
100  if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
101  {
103  }
104  }
105  }
106  else
107  {
108  // Thread will be started later
109  maWaitingForStartThreads.push_back( aThreadData );
110  }
111 
112  return nNewThreadID;
113 }
114 
115 void ThreadManager::RemoveThread( const oslInterlockedCount nThreadID,
116  const bool bThreadFinished )
117 {
118  // --> SAFE ----
119  osl::MutexGuard aGuard(maMutex);
120 
121  std::deque< tThreadData >::iterator aIter =
122  std::find_if( maStartedThreads.begin(), maStartedThreads.end(),
123  ThreadPred( nThreadID ) );
124 
125  if ( aIter != maStartedThreads.end() )
126  {
127  tThreadData aTmpThreadData( *aIter );
128 
129  maStartedThreads.erase( aIter );
130 
131  if ( bThreadFinished )
132  {
133  // release thread as job from thread joiner instance
134  css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
135  if ( rThreadJoiner.is() )
136  {
137  rThreadJoiner->releaseJob( aTmpThreadData.aJob );
138  }
139  else
140  {
141  OSL_FAIL( "<ThreadManager::RemoveThread(..)> - ThreadJoiner already gone!" );
142  }
143  }
144 
145  // Try to start thread from waiting ones
146  TryToStartNewThread( nullptr );
147  }
148  else
149  {
150  aIter = std::find_if( maWaitingForStartThreads.begin(),
151  maWaitingForStartThreads.end(), ThreadPred( nThreadID ) );
152 
153  if ( aIter != maWaitingForStartThreads.end() )
154  {
155  maWaitingForStartThreads.erase( aIter );
156  }
157  }
158  // <-- SAFE ----
159 }
160 
162 {
163  if ( !maWaitingForStartThreads.empty() )
164  {
165  tThreadData aThreadData( maWaitingForStartThreads.front() );
166  maWaitingForStartThreads.pop_front();
167  return StartThread( aThreadData );
168  }
169  else
170  {
171  return false;
172  }
173 }
174 
175 bool ThreadManager::StartThread( const tThreadData& rThreadData )
176 {
177  bool bThreadStarted( false );
178 
179  if ( rThreadData.pThread->create() )
180  {
181  // start of thread successful.
182  bThreadStarted = true;
183 
184  maStartedThreads.push_back( rThreadData );
185 
186  // register thread as job at thread joiner instance
187  css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
188  if ( rThreadJoiner.is() )
189  {
190  rThreadJoiner->registerJob( rThreadData.aJob );
191  }
192  else
193  {
194  OSL_FAIL( "<ThreadManager::StartThread(..)> - ThreadJoiner already gone!" );
195  }
196  }
197  else
198  {
199  // thread couldn't be started.
200  maWaitingForStartThreads.push_front( rThreadData );
201  }
202 
203  return bThreadStarted;
204 }
205 
206 IMPL_LINK_NOARG(ThreadManager, TryToStartNewThread, Timer *, void)
207 {
208  osl::MutexGuard aGuard(maMutex);
209 
210  if ( StartingOfThreadsSuspended() )
211  return;
212 
213  // Try to start thread from waiting ones
214  if ( !StartWaitingThread() )
215  {
216  // No success on starting thread
217  // If no more started threads exist, but still threads are waiting,
218  // setup Timer to start thread from waiting ones
219  if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
220  {
221  maStartNewThreadIdle.Start();
222  }
223  }
224 }
225 
227 {
228  osl::MutexGuard aGuard(maMutex);
229 
231 
232  while ( maStartedThreads.size() < snStartedSize &&
233  !maWaitingForStartThreads.empty() )
234  {
235  if ( !StartWaitingThread() )
236  {
237  // No success on starting thread
238  // If no more started threads exist, but still threads are waiting,
239  // setup Timer to start thread from waiting ones
240  if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
241  {
243  break;
244  }
245  }
246  }
247 }
248 
249 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
void Init()
initialization
ThreadManager(css::uno::Reference< css::util::XJobManager > const &rThreadJoiner)
css::uno::Reference< css::util::XCancellable > aJob
void RemoveThread(const oslInterlockedCount nThreadID, const bool bThreadFinished=false)
std::shared_ptr< ThreadListener > mpThreadListener
oslInterlockedCount mnThreadIDCounter
bool StartWaitingThread()
void ResumeStartingOfThreads()
continues the starting of threads after it has been suspended
std::mutex maMutex
IMPL_LINK_NOARG(ThreadManager, TryToStartNewThread, Timer *, void)
class to manage threads
oslInterlockedCount AddThread(const ::rtl::Reference< ObservableThread > &rThread)
add thread to the thread manager and taking ownership for the thread
bool StartThread(const tThreadData &aThreadData)
std::deque< tThreadData > maWaitingForStartThreads
std::weak_ptr< IFinishedThreadListener > GetThreadListenerWeakRef() const
bool StartingOfThreadsSuspended()
std::deque< tThreadData > maStartedThreads
bool mbStartingOfThreadsSuspended
osl::Mutex maMutex
css::uno::WeakReference< css::util::XJobManager > mrThreadJoiner
oslInterlockedCount nThreadID
void SetInvokeHandler(const Link< Timer *, void > &rLink)
static const std::deque< tThreadData >::size_type snStartedSize
class to manage threads
virtual void Start(bool bStartTimer=true) override
void SetPriority(TaskPriority ePriority)
::rtl::Reference< ObservableThread > pThread
void NotifyAboutFinishedThread(const oslInterlockedCount nThreadID)