2 // This file is (C) 2003-2004 Royce Mitchell III
3 // and released under the LGPL & BSD licenses
7 #include "ThreadPool.h"
9 #include "auto_vector.h"
13 class PoolableThread
: public ActiveObject
16 PoolableThread ( ThreadPoolImpl
& );
25 ThreadPoolImpl
& _pool
;
28 class ThreadPoolLaunchData
36 class AtomicCounter
: public Uncopyable
41 AtomicCounter ( T init
= 0 ) : _t(init
)
44 AtomicCounter ( const AtomicCounter
<T
>& t
)
46 //Mutex::Lock l ( _m ); // don't need to lock since this is a ctor
47 Mutex::Lock
l2 ( t
._m
);
50 const AtomicCounter
<T
>& operator = ( const AtomicCounter
<T
>& t
)
53 Mutex::Lock
l2 ( t
._m
);
63 const AtomicCounter
<T
>& operator ++ ( int )
75 const AtomicCounter
<T
>& operator -- ( int )
81 const AtomicCounter
<T
>& operator += ( T t
)
87 const AtomicCounter
<T
>& operator -= ( T t
)
93 operator const T
& () const
95 //Mutex::Lock l ( _m );
100 //Mutex::Lock l ( _m );
105 class ThreadPoolImpl
: public Uncopyable
108 ThreadPoolImpl() : _isDying(false), _idleThreads(0)
119 while ( _idleThreads
)
121 _threadWaitEvent
.Release();
122 _threadStartEvent
.Wait(); // let thread actually get a grip
126 void Launch ( ThreadPoolFunc
* pFun
, void* pArg
)
128 // this mutex is necessary to make sure we never have a conflict
129 // between checking !_idleThreads and the call to _threadStartEvent.Wait()
130 // basically if 2 threads call Launch() simultaneously, and there is only
131 // 1 idle thread, it's possible that a new thread won't be created to
132 // satisfy the 2nd request until an existing thread finishes.
133 Mutex::Lock
launchlock ( _launchMutex
);
136 ThreadPoolLaunchData
* data
;
138 Mutex::Lock
lock ( _vectorMutex
);
139 if ( !_spareData
.size() )
140 _spareData
.push_back ( new ThreadPoolLaunchData() );
141 data
= _spareData
.pop_back().release();
143 _threads
.push_back ( new PoolableThread(*this) );
148 verify ( _pendingData
.Add ( data
) );
149 _threadWaitEvent
.Release(); // tell a thread to do it's thing...
150 _threadStartEvent
.Wait(); // wait on a thread to pick up the request
153 // functions for threads to call...
154 ThreadPoolLaunchData
* GetPendingData()
156 ThreadPoolLaunchData
* data
= NULL
;
158 _threadWaitEvent
.Wait(); // waits until there's a request
160 _threadStartEvent
.Release(); // tell requester we got it
163 _pendingData
.Get ( data
);
168 void RecycleData ( ThreadPoolLaunchData
* data
)
170 Mutex::Lock
lock ( _vectorMutex
);
171 _spareData
.push_back ( data
);
175 Mutex _vectorMutex
, _launchMutex
;
176 auto_vector
<PoolableThread
> _threads
;
177 auto_vector
<ThreadPoolLaunchData
> _spareData
;
178 CQueueT
<ThreadPoolLaunchData
*> _pendingData
;
179 Event _threadWaitEvent
, _threadStartEvent
;
180 AtomicCounter
<int> _idleThreads
;
183 ///////////////////////////////////////////////////////////////////////////////
186 /*static*/ ThreadPool
& ThreadPool::Instance()
188 static ThreadPool tp
;
192 ThreadPool::ThreadPool() : _pimpl ( new ThreadPoolImpl
)
196 ThreadPool::~ThreadPool()
203 void ThreadPool::Launch ( ThreadPoolFunc
* pFun
, void* pArg
)
205 _pimpl
->Launch ( pFun
, pArg
);
208 int ThreadPool::IdleThreads()
210 return _pimpl
->_idleThreads
;
213 ///////////////////////////////////////////////////////////////////////////////
216 PoolableThread::PoolableThread ( ThreadPoolImpl
& pool
) : _pool(pool
)
221 void PoolableThread::InitThread()
225 void PoolableThread::Run()
227 ThreadPoolLaunchData
* data
;
230 data
= _pool
.GetPendingData(); // enter wait state if none...
231 if ( !data
) // NULL data means kill thread
233 (*data
->pFun
) ( data
->pArg
); // call the function
234 _pool
.RecycleData ( data
);
238 void PoolableThread::FlushThread()