Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / netwerk / base / src / nsIOThreadPool.cpp
blobe52961b2564fbc7ec885a21ef896f16c8e87aa9e
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is Mozilla.
16 * The Initial Developer of the Original Code is IBM Corporation.
17 * Portions created by IBM Corporation are Copyright (C) 2003
18 * IBM Corporation. All Rights Reserved.
20 * Contributor(s):
21 * IBM Corp.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
37 #include "nsIEventTarget.h"
38 #include "nsIServiceManager.h"
39 #include "nsIObserverService.h"
40 #include "nsIObserver.h"
41 #include "nsAutoLock.h"
42 #include "nsCOMPtr.h"
43 #include "prclist.h"
44 #include "prlog.h"
46 #if defined(PR_LOGGING)
48 // NSPR_LOG_MODULES=nsIOThreadPool:5
50 static PRLogModuleInfo *gIOThreadPoolLog = nsnull;
51 #endif
52 #define LOG(args) PR_LOG(gIOThreadPoolLog, PR_LOG_DEBUG, args)
54 // this number specifies the maximum number of threads.
55 #define MAX_THREADS 4
57 // this number specifies how long to wait before killing an idle thread. it's
58 // important to pick a large enough value here to minimize thread churn.
59 #define IDLE_TIMEOUT PR_SecondsToInterval(60)
61 #define PLEVENT_FROM_LINK(_link) \
62 ((PLEvent*) ((char*) (_link) - offsetof(PLEvent, link)))
64 //-----------------------------------------------------------------------------
65 // pool of joinable threads used for general purpose i/o tasks
67 // the main entry point to this class is nsIEventTarget. events posted to
68 // the thread pool are dispatched on one of the threads. a variable number
69 // of threads are maintained. the threads die off if they remain idle for
70 // more than THREAD_IDLE_TIMEOUT. the thread pool shuts down when it receives
71 // the "xpcom-shutdown-threads" event.
72 //-----------------------------------------------------------------------------
74 class nsIOThreadPool : public nsIEventTarget
75 , public nsIObserver
77 public:
78 NS_DECL_ISUPPORTS
79 NS_DECL_NSIEVENTTARGET
80 NS_DECL_NSIOBSERVER
82 nsresult Init();
83 void Shutdown();
85 private:
86 virtual ~nsIOThreadPool();
88 static void ThreadFunc(void *);
90 // mLock protects all (exceptions during Init and Shutdown)
91 PRLock *mLock;
92 PRCondVar *mIdleThreadCV; // notified to wake up an idle thread
93 PRCondVar *mExitThreadCV; // notified when a thread exits
94 PRUint32 mNumThreads; // number of active + idle threads
95 PRUint32 mNumIdleThreads; // number of idle threads
96 PRCList mEventQ; // queue of PLEvent structs
97 PRBool mShutdown; // set to true if shutting down
100 NS_IMPL_THREADSAFE_ISUPPORTS2(nsIOThreadPool, nsIEventTarget, nsIObserver)
102 nsresult
103 nsIOThreadPool::Init()
105 #if defined(PR_LOGGING)
106 if (!gIOThreadPoolLog)
107 gIOThreadPoolLog = PR_NewLogModule("nsIOThreadPool");
108 #endif
110 mNumThreads = 0;
111 mNumIdleThreads = 0;
112 mShutdown = PR_FALSE;
114 mLock = PR_NewLock();
115 if (!mLock)
116 return NS_ERROR_OUT_OF_MEMORY;
118 mIdleThreadCV = PR_NewCondVar(mLock);
119 if (!mIdleThreadCV)
120 return NS_ERROR_OUT_OF_MEMORY;
122 mExitThreadCV = PR_NewCondVar(mLock);
123 if (!mExitThreadCV)
124 return NS_ERROR_OUT_OF_MEMORY;
126 PR_INIT_CLIST(&mEventQ);
128 // We want to shutdown the i/o thread pool at xpcom-shutdown-threads time.
129 nsCOMPtr<nsIObserverService> os = do_GetService("@mozilla.org/observer-service;1");
130 if (os)
131 os->AddObserver(this, "xpcom-shutdown-threads", PR_FALSE);
132 return NS_OK;
135 nsIOThreadPool::~nsIOThreadPool()
137 LOG(("Destroying nsIOThreadPool @%p\n", this));
139 #ifdef DEBUG
140 NS_ASSERTION(PR_CLIST_IS_EMPTY(&mEventQ), "leaking events");
141 NS_ASSERTION(mNumThreads == 0, "leaking thread(s)");
142 #endif
144 if (mIdleThreadCV)
145 PR_DestroyCondVar(mIdleThreadCV);
146 if (mExitThreadCV)
147 PR_DestroyCondVar(mExitThreadCV);
148 if (mLock)
149 PR_DestroyLock(mLock);
152 void
153 nsIOThreadPool::Shutdown()
155 LOG(("nsIOThreadPool::Shutdown\n"));
157 // synchronize with background threads...
159 nsAutoLock lock(mLock);
160 mShutdown = PR_TRUE;
162 PR_NotifyAllCondVar(mIdleThreadCV);
164 while (mNumThreads != 0)
165 PR_WaitCondVar(mExitThreadCV, PR_INTERVAL_NO_TIMEOUT);
169 NS_IMETHODIMP
170 nsIOThreadPool::PostEvent(PLEvent *event)
172 LOG(("nsIOThreadPool::PostEvent [event=%p]\n", event));
174 nsAutoLock lock(mLock);
176 // if we are shutting down, then prevent additional events from being
177 // added to the queue...
178 if (mShutdown)
179 return NS_ERROR_UNEXPECTED;
181 nsresult rv = NS_OK;
183 PR_APPEND_LINK(&event->link, &mEventQ);
185 // now, look for an available idle thread...
186 if (mNumIdleThreads)
187 PR_NotifyCondVar(mIdleThreadCV); // wake up an idle thread
189 // or, try to create a new thread unless we have reached our maximum...
190 else if (mNumThreads < MAX_THREADS) {
191 NS_ADDREF_THIS(); // the thread owns a reference to us
192 mNumThreads++;
193 PRThread *thread = PR_CreateThread(PR_USER_THREAD,
194 ThreadFunc,
195 this,
196 PR_PRIORITY_NORMAL,
197 PR_GLOBAL_THREAD,
198 PR_UNJOINABLE_THREAD,
200 if (!thread) {
201 NS_RELEASE_THIS();
202 mNumThreads--;
203 rv = NS_ERROR_OUT_OF_MEMORY;
206 // else, we expect one of the active threads to process the event queue.
208 return rv;
211 NS_IMETHODIMP
212 nsIOThreadPool::IsOnCurrentThread(PRBool *result)
214 // no one should be calling this method. if this assertion gets hit,
215 // then we need to think carefully about what this method should be
216 // returning.
217 NS_NOTREACHED("nsIOThreadPool::IsOnCurrentThread");
219 // fudging this a bit since we actually cover several threads...
220 *result = PR_FALSE;
221 return NS_OK;
224 NS_IMETHODIMP
225 nsIOThreadPool::Observe(nsISupports *, const char *topic, const PRUnichar *)
227 NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "unexpected topic");
228 Shutdown();
229 return NS_OK;
232 void
233 nsIOThreadPool::ThreadFunc(void *arg)
235 nsIOThreadPool *pool = (nsIOThreadPool *) arg;
237 LOG(("entering ThreadFunc\n"));
240 nsAutoLock lock(pool->mLock);
242 for (;;) {
243 PRIntervalTime start = PR_IntervalNow(), timeout = IDLE_TIMEOUT;
245 // wait for one or more of the following to occur:
246 // (1) the event queue has an event to process
247 // (2) the shutdown flag has been set
248 // (3) the thread has been idle for too long
250 // PR_WaitCondVar will return when any of these conditions is true.
252 while (PR_CLIST_IS_EMPTY(&pool->mEventQ) && !pool->mShutdown) {
253 pool->mNumIdleThreads++;
254 PR_WaitCondVar(pool->mIdleThreadCV, timeout);
255 pool->mNumIdleThreads--;
257 PRIntervalTime delta = PR_IntervalNow() - start;
258 if (delta >= timeout)
259 break;
260 timeout -= delta;
261 start += delta;
264 // if the queue is still empty, then kill this thread (either we
265 // are shutting down or the thread exceeded the idle timeout)...
266 if (PR_CLIST_IS_EMPTY(&pool->mEventQ))
267 break;
269 // handle one event at a time: we don't want this one thread to hog
270 // all the events while other threads may be able to help out ;-)
271 do {
272 PLEvent *event = PLEVENT_FROM_LINK(PR_LIST_HEAD(&pool->mEventQ));
273 PR_REMOVE_AND_INIT_LINK(&event->link);
275 LOG(("event:%p\n", event));
277 // release lock!
278 lock.unlock();
279 PL_HandleEvent(event);
280 lock.lock();
282 while (!PR_CLIST_IS_EMPTY(&pool->mEventQ));
285 // thread is going away...
286 pool->mNumThreads--;
287 PR_NotifyCondVar(pool->mExitThreadCV);
290 // release our reference to the pool
291 NS_RELEASE(pool);
293 LOG(("leaving ThreadFunc\n"));
296 //-----------------------------------------------------------------------------
298 NS_METHOD
299 net_NewIOThreadPool(nsISupports *outer, REFNSIID iid, void **result)
301 nsIOThreadPool *pool = new nsIOThreadPool();
302 if (!pool)
303 return NS_ERROR_OUT_OF_MEMORY;
304 NS_ADDREF(pool);
305 nsresult rv = pool->Init();
306 if (NS_SUCCEEDED(rv))
307 rv = pool->QueryInterface(iid, result);
308 NS_RELEASE(pool);
309 return rv;