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