1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 #include "precompiled_sw.hxx"
24 #include <threadmanager.hxx>
25 #include <errhdl.hxx>
26 
27 #include <algorithm>
28 
29 using namespace ::com::sun::star;
30 
31 /** class to manage threads
32 
33     OD 2007-01-29 #i73788#
34 
35     @author OD
36 */
37 const std::deque< ThreadManager::tThreadData >::size_type ThreadManager::mnStartedSize = 10;
38 
ThreadManager(uno::Reference<util::XJobManager> & rThreadJoiner)39 ThreadManager::ThreadManager( uno::Reference< util::XJobManager >& rThreadJoiner )
40     : maMutex(),
41       mrThreadJoiner( rThreadJoiner ),
42       mpThreadListener(),
43       mnThreadIDCounter( 0 ),
44       maWaitingForStartThreads(),
45       maStartedThreads(),
46       maStartNewThreadTimer(),
47       mbStartingOfThreadsSuspended( false )
48 {
49 }
50 
Init()51 void ThreadManager::Init()
52 {
53     mpThreadListener.reset( new ThreadListener( *this ) );
54 
55     maStartNewThreadTimer.SetTimeout( 2000 );
56     maStartNewThreadTimer.SetTimeoutHdl( LINK( this, ThreadManager, TryToStartNewThread ) );
57 }
58 
~ThreadManager()59 ThreadManager::~ThreadManager()
60 {
61     maWaitingForStartThreads.clear();
62     maStartedThreads.clear();
63 }
64 
GetThreadListenerWeakRef()65 boost::weak_ptr< IFinishedThreadListener > ThreadManager::GetThreadListenerWeakRef()
66 {
67     return mpThreadListener;
68 }
69 
NotifyAboutFinishedThread(const oslInterlockedCount nThreadID)70 void ThreadManager::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
71 {
72     RemoveThread( nThreadID, true );
73 }
74 
AddThread(const rtl::Reference<ObservableThread> & rThread)75 oslInterlockedCount ThreadManager::AddThread(
76                             const rtl::Reference< ObservableThread >& rThread )
77 
78 {
79     osl::MutexGuard aGuard(maMutex);
80 
81     // create new thread
82     tThreadData aThreadData;
83     oslInterlockedCount nNewThreadID( RetrieveNewThreadID() );
84     {
85         aThreadData.nThreadID = nNewThreadID;
86 
87         aThreadData.pThread = rThread;
88         aThreadData.aJob = new CancellableJob( aThreadData.pThread );
89 
90         aThreadData.pThread->setPriority( osl_Thread_PriorityBelowNormal );
91         mpThreadListener->ListenToThread( aThreadData.nThreadID,
92                                           *(aThreadData.pThread) );
93     }
94 
95     // add thread to manager
96     if ( maStartedThreads.size() < mnStartedSize &&
97          !StartingOfThreadsSuspended() )
98     {
99         // Try to start thread
100         if ( !StartThread( aThreadData ) )
101         {
102             // No success on starting thread
103             // If no more started threads exist, but still threads are waiting,
104             // setup Timer to start thread from waiting ones
105             if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
106             {
107                 maStartNewThreadTimer.Start();
108             }
109         }
110     }
111     else
112     {
113         // Thread will be started later
114         maWaitingForStartThreads.push_back( aThreadData );
115     }
116 
117     return nNewThreadID;
118 }
119 
RemoveThread(const oslInterlockedCount nThreadID,const bool bThreadFinished)120 void ThreadManager::RemoveThread( const oslInterlockedCount nThreadID,
121                                   const bool bThreadFinished )
122 {
123     // --> SAFE ----
124     osl::MutexGuard aGuard(maMutex);
125 
126     std::deque< tThreadData >::iterator aIter =
127                 std::find_if( maStartedThreads.begin(), maStartedThreads.end(),
128                               ThreadPred( nThreadID ) );
129 
130     if ( aIter != maStartedThreads.end() )
131     {
132         tThreadData aTmpThreadData( (*aIter) );
133 
134         maStartedThreads.erase( aIter );
135 
136         if ( bThreadFinished )
137         {
138             // release thread as job from thread joiner instance
139             ::com::sun::star::uno::Reference< ::com::sun::star::util::XJobManager > rThreadJoiner( mrThreadJoiner );
140             if ( rThreadJoiner.is() )
141             {
142                 rThreadJoiner->releaseJob( aTmpThreadData.aJob );
143             }
144             else
145             {
146                 ASSERT( false, "<ThreadManager::RemoveThread(..)> - ThreadJoiner already gone!" );
147             }
148         }
149 
150         // Try to start thread from waiting ones
151         TryToStartNewThread( 0 );
152     }
153     else
154     {
155         aIter = std::find_if( maWaitingForStartThreads.begin(),
156                               maWaitingForStartThreads.end(), ThreadPred( nThreadID ) );
157 
158         if ( aIter != maWaitingForStartThreads.end() )
159         {
160             maWaitingForStartThreads.erase( aIter );
161         }
162     }
163     // <-- SAFE ----
164 }
165 
StartWaitingThread()166 bool ThreadManager::StartWaitingThread()
167 {
168     if ( !maWaitingForStartThreads.empty() )
169     {
170         tThreadData aThreadData( maWaitingForStartThreads.front() );
171         maWaitingForStartThreads.pop_front();
172         return StartThread( aThreadData );
173     }
174     else
175     {
176         return false;
177     }
178 }
179 
StartThread(const tThreadData & rThreadData)180 bool ThreadManager::StartThread( const tThreadData& rThreadData )
181 {
182     bool bThreadStarted( false );
183 
184     if ( rThreadData.pThread->create() )
185     {
186         // start of thread successful.
187         bThreadStarted = true;
188 
189         maStartedThreads.push_back( rThreadData );
190 
191         // register thread as job at thread joiner instance
192         ::com::sun::star::uno::Reference< ::com::sun::star::util::XJobManager > rThreadJoiner( mrThreadJoiner );
193         if ( rThreadJoiner.is() )
194         {
195             rThreadJoiner->registerJob( rThreadData.aJob );
196         }
197         else
198         {
199             ASSERT( false, "<ThreadManager::StartThread(..)> - ThreadJoiner already gone!" );
200         }
201     }
202     else
203     {
204         // thread couldn't be started.
205         maWaitingForStartThreads.push_front( rThreadData );
206     }
207 
208     return bThreadStarted;
209 }
210 
IMPL_LINK(ThreadManager,TryToStartNewThread,Timer *,EMPTYARG)211 IMPL_LINK( ThreadManager, TryToStartNewThread, Timer *, EMPTYARG )
212 {
213     osl::MutexGuard aGuard(maMutex);
214 
215     if ( !StartingOfThreadsSuspended() )
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                 maStartNewThreadTimer.Start();
226             }
227         }
228     }
229 
230     return sal_True;
231 }
232 
ResumeStartingOfThreads()233 void ThreadManager::ResumeStartingOfThreads()
234 {
235     osl::MutexGuard aGuard(maMutex);
236 
237     mbStartingOfThreadsSuspended = false;
238 
239     while ( maStartedThreads.size() < mnStartedSize &&
240             !maWaitingForStartThreads.empty() )
241     {
242         if ( !StartWaitingThread() )
243         {
244             // No success on starting thread
245             // If no more started threads exist, but still threads are waiting,
246             // setup Timer to start thread from waiting ones
247             if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
248             {
249                 maStartNewThreadTimer.Start();
250                 break;
251             }
252         }
253     }
254 }
255