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  std::unique_lock 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  std::unique_lock 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  aGuard.unlock();
147  TryToStartNewThread( nullptr );
148  }
149  else
150  {
151  aIter = std::find_if( maWaitingForStartThreads.begin(),
152  maWaitingForStartThreads.end(), ThreadPred( nThreadID ) );
153 
154  if ( aIter != maWaitingForStartThreads.end() )
155  {
156  maWaitingForStartThreads.erase( aIter );
157  }
158  }
159  // <-- SAFE ----
160 }
161 
163 {
164  if ( !maWaitingForStartThreads.empty() )
165  {
166  tThreadData aThreadData( maWaitingForStartThreads.front() );
167  maWaitingForStartThreads.pop_front();
168  return StartThread( aThreadData );
169  }
170  else
171  {
172  return false;
173  }
174 }
175 
176 bool ThreadManager::StartThread( const tThreadData& rThreadData )
177 {
178  bool bThreadStarted( false );
179 
180  if ( rThreadData.pThread->create() )
181  {
182  // start of thread successful.
183  bThreadStarted = true;
184 
185  maStartedThreads.push_back( rThreadData );
186 
187  // register thread as job at thread joiner instance
188  css::uno::Reference< css::util::XJobManager > rThreadJoiner( mrThreadJoiner );
189  if ( rThreadJoiner.is() )
190  {
191  rThreadJoiner->registerJob( rThreadData.aJob );
192  }
193  else
194  {
195  OSL_FAIL( "<ThreadManager::StartThread(..)> - ThreadJoiner already gone!" );
196  }
197  }
198  else
199  {
200  // thread couldn't be started.
201  maWaitingForStartThreads.push_front( rThreadData );
202  }
203 
204  return bThreadStarted;
205 }
206 
207 IMPL_LINK_NOARG(ThreadManager, TryToStartNewThread, Timer *, void)
208 {
209  std::unique_lock aGuard(maMutex);
210 
211  if ( mbStartingOfThreadsSuspended )
212  return;
213 
214  // Try to start thread from waiting ones
215  if ( !StartWaitingThread() )
216  {
217  // No success on starting thread
218  // If no more started threads exist, but still threads are waiting,
219  // setup Timer to start thread from waiting ones
220  if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
221  {
222  maStartNewThreadIdle.Start();
223  }
224  }
225 }
226 
228 {
229  std::unique_lock aGuard(maMutex);
230 
232 
233  while ( maStartedThreads.size() < snStartedSize &&
234  !maWaitingForStartThreads.empty() )
235  {
236  if ( !StartWaitingThread() )
237  {
238  // No success on starting thread
239  // If no more started threads exist, but still threads are waiting,
240  // setup Timer to start thread from waiting ones
241  if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
242  {
244  break;
245  }
246  }
247  }
248 }
249 
250 /* 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
std::mutex maMutex
std::deque< tThreadData > maStartedThreads
bool mbStartingOfThreadsSuspended
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)
bool m_bDetectedRangeSegmentation false