1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 #include "precompiled_sw.hxx"
28 #include <threadmanager.hxx>
29 #include <errhdl.hxx>
30 
31 #include <algorithm>
32 
33 using namespace ::com::sun::star;
34 
35 /** class to manage threads
36 
37     OD 2007-01-29 #i73788#
38 
39     @author OD
40 */
41 const std::deque< ThreadManager::tThreadData >::size_type ThreadManager::mnStartedSize = 10;
42 
43 ThreadManager::ThreadManager( uno::Reference< util::XJobManager >& rThreadJoiner )
44     : maMutex(),
45       mrThreadJoiner( rThreadJoiner ),
46       mpThreadListener(),
47       mnThreadIDCounter( 0 ),
48       maWaitingForStartThreads(),
49       maStartedThreads(),
50       maStartNewThreadTimer(),
51       mbStartingOfThreadsSuspended( false )
52 {
53 }
54 
55 void ThreadManager::Init()
56 {
57     mpThreadListener.reset( new ThreadListener( *this ) );
58 
59     maStartNewThreadTimer.SetTimeout( 2000 );
60     maStartNewThreadTimer.SetTimeoutHdl( LINK( this, ThreadManager, TryToStartNewThread ) );
61 }
62 
63 ThreadManager::~ThreadManager()
64 {
65     maWaitingForStartThreads.clear();
66     maStartedThreads.clear();
67 }
68 
69 boost::weak_ptr< IFinishedThreadListener > ThreadManager::GetThreadListenerWeakRef()
70 {
71     return mpThreadListener;
72 }
73 
74 void ThreadManager::NotifyAboutFinishedThread( const oslInterlockedCount nThreadID )
75 {
76     RemoveThread( nThreadID, true );
77 }
78 
79 oslInterlockedCount ThreadManager::AddThread(
80                             const rtl::Reference< ObservableThread >& rThread )
81 
82 {
83     osl::MutexGuard aGuard(maMutex);
84 
85     // create new thread
86     tThreadData aThreadData;
87     oslInterlockedCount nNewThreadID( RetrieveNewThreadID() );
88     {
89         aThreadData.nThreadID = nNewThreadID;
90 
91         aThreadData.pThread = rThread;
92         aThreadData.aJob = new CancellableJob( aThreadData.pThread );
93 
94         aThreadData.pThread->setPriority( osl_Thread_PriorityBelowNormal );
95         mpThreadListener->ListenToThread( aThreadData.nThreadID,
96                                           *(aThreadData.pThread) );
97     }
98 
99     // add thread to manager
100     if ( maStartedThreads.size() < mnStartedSize &&
101          !StartingOfThreadsSuspended() )
102     {
103         // Try to start thread
104         if ( !StartThread( aThreadData ) )
105         {
106             // No success on starting thread
107             // If no more started threads exist, but still threads are waiting,
108             // setup Timer to start thread from waiting ones
109             if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
110             {
111                 maStartNewThreadTimer.Start();
112             }
113         }
114     }
115     else
116     {
117         // Thread will be started later
118         maWaitingForStartThreads.push_back( aThreadData );
119     }
120 
121     return nNewThreadID;
122 }
123 
124 void ThreadManager::RemoveThread( const oslInterlockedCount nThreadID,
125                                   const bool bThreadFinished )
126 {
127     // --> SAFE ----
128     osl::MutexGuard aGuard(maMutex);
129 
130     std::deque< tThreadData >::iterator aIter =
131                 std::find_if( maStartedThreads.begin(), maStartedThreads.end(),
132                               ThreadPred( nThreadID ) );
133 
134     if ( aIter != maStartedThreads.end() )
135     {
136         tThreadData aTmpThreadData( (*aIter) );
137 
138         maStartedThreads.erase( aIter );
139 
140         if ( bThreadFinished )
141         {
142             // release thread as job from thread joiner instance
143             ::com::sun::star::uno::Reference< ::com::sun::star::util::XJobManager > rThreadJoiner( mrThreadJoiner );
144             if ( rThreadJoiner.is() )
145             {
146                 rThreadJoiner->releaseJob( aTmpThreadData.aJob );
147             }
148             else
149             {
150                 ASSERT( false, "<ThreadManager::RemoveThread(..)> - ThreadJoiner already gone!" );
151             }
152         }
153 
154         // Try to start thread from waiting ones
155         TryToStartNewThread( 0 );
156     }
157     else
158     {
159         aIter = std::find_if( maWaitingForStartThreads.begin(),
160                               maWaitingForStartThreads.end(), ThreadPred( nThreadID ) );
161 
162         if ( aIter != maWaitingForStartThreads.end() )
163         {
164             maWaitingForStartThreads.erase( aIter );
165         }
166     }
167     // <-- SAFE ----
168 }
169 
170 bool ThreadManager::StartWaitingThread()
171 {
172     if ( !maWaitingForStartThreads.empty() )
173     {
174         tThreadData aThreadData( maWaitingForStartThreads.front() );
175         maWaitingForStartThreads.pop_front();
176         return StartThread( aThreadData );
177     }
178     else
179     {
180         return false;
181     }
182 }
183 
184 bool ThreadManager::StartThread( const tThreadData& rThreadData )
185 {
186     bool bThreadStarted( false );
187 
188     if ( rThreadData.pThread->create() )
189     {
190         // start of thread successful.
191         bThreadStarted = true;
192 
193         maStartedThreads.push_back( rThreadData );
194 
195         // register thread as job at thread joiner instance
196         ::com::sun::star::uno::Reference< ::com::sun::star::util::XJobManager > rThreadJoiner( mrThreadJoiner );
197         if ( rThreadJoiner.is() )
198         {
199             rThreadJoiner->registerJob( rThreadData.aJob );
200         }
201         else
202         {
203             ASSERT( false, "<ThreadManager::StartThread(..)> - ThreadJoiner already gone!" );
204         }
205     }
206     else
207     {
208         // thread couldn't be started.
209         maWaitingForStartThreads.push_front( rThreadData );
210     }
211 
212     return bThreadStarted;
213 }
214 
215 IMPL_LINK( ThreadManager, TryToStartNewThread, Timer *, EMPTYARG )
216 {
217     osl::MutexGuard aGuard(maMutex);
218 
219     if ( !StartingOfThreadsSuspended() )
220     {
221         // Try to start thread from waiting ones
222         if ( !StartWaitingThread() )
223         {
224             // No success on starting thread
225             // If no more started threads exist, but still threads are waiting,
226             // setup Timer to start thread from waiting ones
227             if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
228             {
229                 maStartNewThreadTimer.Start();
230             }
231         }
232     }
233 
234     return sal_True;
235 }
236 
237 void ThreadManager::ResumeStartingOfThreads()
238 {
239     osl::MutexGuard aGuard(maMutex);
240 
241     mbStartingOfThreadsSuspended = false;
242 
243     while ( maStartedThreads.size() < mnStartedSize &&
244             !maWaitingForStartThreads.empty() )
245     {
246         if ( !StartWaitingThread() )
247         {
248             // No success on starting thread
249             // If no more started threads exist, but still threads are waiting,
250             // setup Timer to start thread from waiting ones
251             if ( maStartedThreads.empty() && !maWaitingForStartThreads.empty() )
252             {
253                 maStartNewThreadTimer.Start();
254                 break;
255             }
256         }
257     }
258 }
259